【Dubbo源码阅读系列】服务暴露之远程暴露

 

引言

什么叫 远程暴露 ?试着想象着这么一种场景:假设我们新增了一台服务器 A,专门用于发送短信提示给指定用户。那么问题来了,我们的 Message 服务上线之后,应该如何告知调用方服务器,服务器 A 提供了 Message 功能?那么我们是不是可以把目前已提供的服务暴露在一个地方,让调用方知道某台机器提供了某个特定功能?带着这样的假设,我们今天就来聊聊 Dubbo 服务暴露之远程暴露!!

服务远程暴露

先回顾一下上篇文章,上篇文章我们聊到了 ServiceConfig 的 export() 方法,并且对服务的本地暴露内容进行了分析,今天我们接着这块内容讲讲服务暴露之远程暴露。

// export to remote if the config is not local (export to local only when config is local) private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs) {     ...     if (!Constants.SCOPE_LOCAL.equalsIgnoreCase(scope)) {         if (logger.isInfoEnabled()) {             logger.info("Export dubbo service " + interfaceClass.getName() + " to url " + url);         }         if (registryURLs != null && !registryURLs.isEmpty()) {             for (URL registryURL : registryURLs) {                 url = url.addParameterIfAbsent(Constants.DYNAMIC_KEY, registryURL.getParameter(Constants.DYNAMIC_KEY));                 // 为了帮助大家阅读,省略部分代码...                 Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString()));                 DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);                  Exporter<?> exporter = protocol.export(wrapperInvoker);                 exporters.add(exporter);             }         } else {             ...         }     }     ... }

这里我们只关注核心代码:

Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString())); DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this); Exporter<?> exporter = protocol.export(wrapperInvoker);

invoker 对象的构建

先来看看 invoker 对象是怎么创建的!这里涉及到了 Dubbo SPI 机制,调用流程大致为
StubProxyFactoryWrapper.getInvoker() ==> JavassistProxyFactory.getInvoker()
详细看下 JavassistProxyFactory 类的 getInvoker 方法

public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {     // TODO Wrapper cannot handle this scenario correctly: the classname contains '$'     final Wrapper wrapper = Wrapper.getWrapper(proxy.getClass().getName().indexOf('$') < 0 ? proxy.getClass() : type);     return new AbstractProxyInvoker<T>(proxy, type, url) {         @Override         protected Object doInvoke(T proxy, String methodName,                 Class<?>[] parameterTypes,                 Object[] arguments) throws Throwable {             return wrapper.invokeMethod(proxy, methodName, parameterTypes, arguments);         }     }; }

值得我们重点注意的是 Wrapper 类的 getWrapper() 方法!!

public static Wrapper getWrapper(Class<?> c) {     while (ClassGenerator.isDynamicClass(c)) // can not wrapper on dynamic class.     {         c = c.getSuperclass();     }      if (c == Object.class) {         return OBJECT_WRAPPER;     }      Wrapper ret = WRAPPER_MAP.get(c);     if (ret == null) {         ret = makeWrapper(c);         WRAPPER_MAP.put(c, ret);     }      return ret; }

这里会使用参数 c 作为 key 值从 WRAPPER_MAP 缓存中取值,如果没有对应的 value 值,会调用 makeWrapper() 方法借助 javassist 技术构建一个 Wrapper 包装类。假设当前参数 c 的值为 demoService,那么最后生成的动态类为:

public class Wrapper0 extends Wrapper implements DC {     public static String[] pns;     public static Map pts;     public static String[] mns;     public static String[] dmns;     public stat
                    
50000+
5万行代码练就真实本领
17年
创办于2008年老牌培训机构
1000+
合作企业
98%
就业率

联系我们

电话咨询

0532-85025005

扫码添加微信