引言

在使用Dubbo的时候你一定会好奇它是怎么实现RPC的,而要了解它的调用过程,必然需要先了解其服务发布/订阅的过程,本篇将详细讨论Dubbo的发布过程。

源码分析

发布服务

新学Dubbo大都会比较疑惑,服务启动时的入口在哪?是如何加载我们的配置的?由于Dubbo是基于Spring的自定义扩展标签来实现配置的,而发布服务时我们需要配置dubbo:service标签,因此我们可以从这里入手。

首先我们需要知道Spring的自定义扩展标签由xsd后缀的文件及spring.schemas(自定义标签)、spring.handlers及DubboNamespaceHandler(NamespaceHandler注册标签的命名空间,这个文件和类类似SPI机制)、以及DubboBeanDefinitionParser(标签解析类)组成。我们可以在resources/META-INF路径下找到spring.handlers、dubbo.xsd以及spring.schemas,spring.handlers文件中可以看到DubboNamespaceHandler的位置,直接看这个类:

  1. public class DubboNamespaceHandler extends NamespaceHandlerSupport {
  2. static {
  3. Version.checkDuplicate(DubboNamespaceHandler.class);
  4. }
  5. public void init() {
  6. registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(ApplicationConfig.class, true));
  7. registerBeanDefinitionParser("module", new DubboBeanDefinitionParser(ModuleConfig.class, true));
  8. registerBeanDefinitionParser("registry", new DubboBeanDefinitionParser(RegistryConfig.class, true));
  9. registerBeanDefinitionParser("monitor", new DubboBeanDefinitionParser(MonitorConfig.class, true));
  10. registerBeanDefinitionParser("provider", new DubboBeanDefinitionParser(ProviderConfig.class, true));
  11. registerBeanDefinitionParser("consumer", new DubboBeanDefinitionParser(ConsumerConfig.class, true));
  12. registerBeanDefinitionParser("protocol", new DubboBeanDefinitionParser(ProtocolConfig.class, true));
  13. registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true));
  14. registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, false));
  15. registerBeanDefinitionParser("annotation", new DubboBeanDefinitionParser(AnnotationBean.class, true));
  16. }
  17. }

主要逻辑在init方法,该方法又会调用registerBeanDefinitionParser方法注册一个个标签解析器,并通过DubboBeanDefinitionParser将配置解析到对应类的属性中,这里我们是分析服务发布的原理,因此直接找到service标签对应的类ServiceBean

  1. public class ServiceBean<T> extends ServiceConfig<T> implements InitializingBean, DisposableBean, ApplicationContextAware, ApplicationListener, BeanNameAware

该类继承了ServiceConfig(实际的配置类)并实现了很多的接口,每个接口的作用如下:

  • InitializingBean:实现了该接口的类会在bean初始化完成后调用afterPropertiesSet方法。
  • DisposableBean:实现该接口的类会在bean销毁时调用destroy方法
  • ApplicationContextAware:容器初始化完成后,会自动将applicationContext注入到该接口的子类
  • ApplicationListener:容器初始化完成后会自动触发调用onApplicationEvent方法
  • BeanNameAware:容器初始化完成后会调用setBeanName将容器中的唯一id告诉给bean本身

而服务的发布逻辑则主要是通过onApplicationEventafterPropertiesSet实现的。而具体使用哪一个方式来发布流程则是根据delay配置来决定的,该属性表示延迟发布服务的毫秒数,即是在ServiceBean初始化完成后就发布还是延迟相应时间后再发布,-1和null表示延迟到Spring容器启动完成后发布。在本版本中,默认是null,即等到Spring容器启动完成后发布服务(在后续版本中默认值改为0,即立即发布服务),所以直接看onApplicationEvent方法:

  1. public void onApplicationEvent(ApplicationEvent event) {
  2. if (ContextRefreshedEvent.class.getName().equals(event.getClass().getName())) {
  3. // 是否延迟发布&&是否已经发布&&是否已经取消发布
  4. if (isDelay() && ! isExported() && ! isUnexported()) {
  5. if (logger.isInfoEnabled()) {
  6. logger.info("The service ready on spring started. service: " + getInterface());
  7. }
  8. export();
  9. }
  10. }
  11. }

主要是调用父类ServiceConfig的export方法发布服务:

  1. public synchronized void export() {
  2. if (provider != null) {
  3. if (export == null) {
  4. export = provider.getExport();
  5. }
  6. if (delay == null) {
  7. delay = provider.getDelay();
  8. }
  9. }
  10. if (export != null && ! export.booleanValue()) {
  11. return;
  12. }
  13. if (delay != null && delay > 0) {
  14. Thread thread = new Thread(new Runnable() {
  15. public void run() {
  16. try {
  17. Thread.sleep(delay);
  18. } catch (Throwable e) {
  19. }
  20. doExport();
  21. }
  22. });
  23. thread.setDaemon(true);
  24. thread.setName("DelayExportServiceThread");
  25. thread.start();
  26. } else {
  27. doExport();
  28. }
  29. }

在该方法中我们可以看到具体延迟多少毫秒发布服务是通过新建线程并睡眠相应的毫秒数实现的,如果没有配置延迟发布就直接调用doExport方法发布,而此方法中大部分逻辑都是在检验配置,关键点是调用的doExportUrls方法:

  1. private void doExportUrls() {
  2. // 从<dubbo:registry>配置中加载注册中心的地址
  3. List<URL> registryURLs = loadRegistries(true);
  4. // 多协议发布则会有多个protocol
  5. for (ProtocolConfig protocolConfig : protocols) {
  6. doExportUrlsFor1Protocol(protocolConfig, registryURLs);
  7. }
  8. }

这里主要是从配置中加载注册中心的地址,并循环调用doExportUrlsFor1Protocol方法处理多协议配置,该方法很长,从开头我们就可以看出若未配置协议,默认使用dubbo协议:

  1. String name = protocolConfig.getName();
  2. if (name == null || name.length() == 0) {
  3. name = "dubbo";
  4. }

接着大部分代码是获取服务提供者的主机IP以及组装配置信息,最关键的是服务发布的逻辑:

  1. // 获取上下文配置
  2. String contextPath = protocolConfig.getContextpath();
  3. if ((contextPath == null || contextPath.length() == 0) && provider != null) {
  4. contextPath = provider.getContextpath();
  5. }
  6. URL url = new URL(name, host, port, (contextPath == null || contextPath.length() == 0 ? "" : contextPath + "/") + path, map);
  7. if (ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class)
  8. .hasExtension(url.getProtocol())) {
  9. url = ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class)
  10. .getExtension(url.getProtocol()).getConfigurator(url).configure(url);
  11. }
  12. String scope = url.getParameter(Constants.SCOPE_KEY);
  13. //配置为none不暴露
  14. if (! Constants.SCOPE_NONE.toString().equalsIgnoreCase(scope)) {
  15. //配置不是remote的情况下做本地暴露 (配置为remote,则表示只暴露远程服务)
  16. if (!Constants.SCOPE_REMOTE.toString().equalsIgnoreCase(scope)) {
  17. exportLocal(url);
  18. }
  19. //如果配置不是local则暴露为远程服务.(配置为local,则表示只暴露本地服务)
  20. if (! Constants.SCOPE_LOCAL.toString().equalsIgnoreCase(scope) ){
  21. if (logger.isInfoEnabled()) {
  22. logger.info("Export dubbo service " + interfaceClass.getName() + " to url " + url);
  23. }
  24. if (registryURLs != null && registryURLs.size() > 0
  25. && url.getParameter("register", true)) {
  26. for (URL registryURL : registryURLs) {
  27. url = url.addParameterIfAbsent("dynamic", registryURL.getParameter("dynamic"));
  28. URL monitorUrl = loadMonitor(registryURL);
  29. if (monitorUrl != null) {
  30. url = url.addParameterAndEncoded(Constants.MONITOR_KEY, monitorUrl.toFullString());
  31. }
  32. if (logger.isInfoEnabled()) {
  33. logger.info("Register dubbo service " + interfaceClass.getName() + " url " + url + " to registry " + registryURL);
  34. }
  35. Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString()));
  36. Exporter<?> exporter = protocol.export(invoker);
  37. exporters.add(exporter);
  38. }
  39. } else {
  40. Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, url);
  41. Exporter<?> exporter = protocol.export(invoker);
  42. exporters.add(exporter);
  43. }
  44. }
  45. }

首先从url中获取到scop信息,即是否发布服务:none不发布、remote只发布远程服务、local只发布本地jvm服务、null表示既然发布远程又要发布本地服务。首先来看本地服务发布:

  1. private void exportLocal(URL url) {
  2. if (!Constants.LOCAL_PROTOCOL.equalsIgnoreCase(url.getProtocol())) {
  3. URL local = URL.valueOf(url.toFullString())
  4. .setProtocol(Constants.LOCAL_PROTOCOL)
  5. .setHost(NetUtils.LOCALHOST)
  6. .setPort(0);
  7. Exporter<?> exporter = protocol.export(
  8. proxyFactory.getInvoker(ref, (Class) interfaceClass, local));
  9. exporters.add(exporter);
  10. logger.info("Export dubbo service " + interfaceClass.getName() +" to local registry");
  11. }
  12. }

本地服务是通过protocol.export发布的,这个protocol是通过下面的代码获取的:

  1. private static final Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();

这个是上一篇文章的主要内容自适应扩展,所以这里是Protocol$Adpative对象,而在这个类中主要是通过协议类型获取相应的扩展类,那这里的协议是什么呢?在exportLocal方法中可以看到setProtocol(Constants.LOCAL_PROTOCOL),而LOCAL_PROTOCOL=injvm,所以这里的export最终会进入到InjvmProtocol.export方法中,但不仅仅是这么简单,在分析SPI源码时,在ExtensionLoader.createExtension方法中有这样一段代码:

  1. // loadFile中会判断当前扩展类是否包含有参构造函数,有的话就就将其赋值给cachedWrapperClasses
  2. Set<Class<?>> wrapperClasses = cachedWrapperClasses;
  3. if (wrapperClasses != null && wrapperClasses.size() > 0) {
  4. for (Class<?> wrapperClass : wrapperClasses) {
  5. instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
  6. }
  7. }

就是使用当前扩展接口的装饰扩展类(指包含有参构造,且参数为当前的扩展接口类型)装饰当前扩展类,而Protocol包含两个装饰类:ProtocolFilterWrapperProtocolListenerWrapper。所以这里实际应该为ProtocolListenerWrapper(ProtocolFilterWrapper(InjvmProtocol)),这里不用细看,filter和listener是过滤器和监听器,InjvmProtocol就是发布到jvm中,供同一个jvm的消费者调用,重点还是在远程发布服务中。同样的,远程发布服务一样是调用了protocol.export方法:

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

Invoker对象暂时忽略,这里就先简单的看作是代理对象,后面会详细分析。关键是这里应该调用哪一个扩展的export方法呢?根据刚才的分析我们需要看url协议是什么,而这里是的协议是registry,所以会进入到RegistryProtocol(注意也是被包装过的)的export方法中(我想现在你应该能体会到自适应扩展的妙用了):

  1. public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {
  2. // 启动本地服务
  3. final ExporterChangeableWrapper<T> exporter = doLocalExport(originInvoker);
  4. // 将远程服务url注册到zookeeper中
  5. final Registry registry = getRegistry(originInvoker);
  6. final URL registedProviderUrl = getRegistedProviderUrl(originInvoker);
  7. registry.register(registedProviderUrl);
  8. .......
  9. }

这个方法就是暴露本地服务,并将服务信息注册到zookeeper,要了解Dubbo底层是如何通信的,就需要详细分析doLocalExport

  1. private <T> ExporterChangeableWrapper<T> doLocalExport(final Invoker<T> originInvoker){
  2. // 拿到provider的url信息,如dubbo://.....
  3. String key = getCacheKey(originInvoker);
  4. // 已经暴露过的服务会缓存到bounds中
  5. ExporterChangeableWrapper<T> exporter = (ExporterChangeableWrapper<T>) bounds.get(key);
  6. if (exporter == null) {
  7. synchronized (bounds) {
  8. exporter = (ExporterChangeableWrapper<T>) bounds.get(key);
  9. if (exporter == null) {
  10. final Invoker<?> invokerDelegete = new InvokerDelegete<T>(originInvoker, getProviderUrl(originInvoker));
  11. exporter = new ExporterChangeableWrapper<T>((Exporter<T>)protocol.export(invokerDelegete), originInvoker);
  12. bounds.put(key, exporter);
  13. }
  14. }
  15. }
  16. return (ExporterChangeableWrapper<T>) exporter;
  17. }

又是protocol.export,因为这里是dubbo协议,所以直接看DubboProtocol.export方法:

  1. public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
  2. URL url = invoker.getUrl();
  3. // 这里是获取到发布的接口
  4. String key = serviceKey(url);
  5. DubboExporter<T> exporter = new DubboExporter<T>(invoker, key, exporterMap);
  6. exporterMap.put(key, exporter);
  7. ......
  8. // 启动服务
  9. openServer(url);
  10. return exporter;
  11. }
  12. private void openServer(URL url) {
  13. // find server.
  14. String key = url.getAddress();
  15. //client 也可以暴露一个只有server可以调用的服务。
  16. boolean isServer = url.getParameter(Constants.IS_SERVER_KEY,true);
  17. if (isServer) {
  18. // 先从缓存中获取server,没有就创建并缓存
  19. ExchangeServer server = serverMap.get(key);
  20. if (server == null) {
  21. serverMap.put(key, createServer(url));
  22. } else {
  23. //server支持reset,配合override功能使用
  24. server.reset(url);
  25. }
  26. }
  27. }
  28. private ExchangeServer createServer(URL url) {
  29. //默认开启server关闭时发送readonly事件
  30. url = url.addParameterIfAbsent(Constants.CHANNEL_READONLYEVENT_SENT_KEY, Boolean.TRUE.toString());
  31. //默认开启heartbeat
  32. url = url.addParameterIfAbsent(Constants.HEARTBEAT_KEY, String.valueOf(Constants.DEFAULT_HEARTBEAT));
  33. String str = url.getParameter(Constants.SERVER_KEY, Constants.DEFAULT_REMOTING_SERVER);
  34. if (str != null && str.length() > 0 && ! ExtensionLoader.getExtensionLoader(Transporter.class).hasExtension(str))
  35. throw new RpcException("Unsupported server type: " + str + ", url: " + url);
  36. url = url.addParameter(Constants.CODEC_KEY, Version.isCompatibleVersion() ? COMPATIBLE_CODEC_NAME : DubboCodec.NAME);
  37. ExchangeServer server;
  38. try {
  39. server = Exchangers.bind(url, requestHandler);
  40. } catch (RemotingException e) {
  41. throw new RpcException("Fail to start server(url: " + url + ") " + e.getMessage(), e);
  42. }
  43. str = url.getParameter(Constants.CLIENT_KEY);
  44. if (str != null && str.length() > 0) {
  45. Set<String> supportedTypes = ExtensionLoader.getExtensionLoader(Transporter.class).getSupportedExtensions();
  46. if (!supportedTypes.contains(str)) {
  47. throw new RpcException("Unsupported client type: " + str);
  48. }
  49. }
  50. return server;
  51. }

最终是通过Exchangers.bind创建的server,而该方法同样是通过调用相应扩展的bind方法来开启服务,而Exchanger只有一个扩展HeaderExchanger

  1. public ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException {
  2. return new HeaderExchangeServer(Transporters.bind(url, new DecodeHandler(new HeaderExchangeHandler(handler))));
  3. }
  4. public HeaderExchangeServer(Server server) {
  5. if (server == null) {
  6. throw new IllegalArgumentException("server == null");
  7. }
  8. this.server = server;
  9. this.heartbeat = server.getUrl().getParameter(Constants.HEARTBEAT_KEY, 0);
  10. this.heartbeatTimeout = server.getUrl().getParameter(Constants.HEARTBEAT_TIMEOUT_KEY, heartbeat * 3);
  11. if (heartbeatTimeout < heartbeat * 2) {
  12. throw new IllegalStateException("heartbeatTimeout < heartbeatInterval * 2");
  13. }
  14. startHeatbeatTimer();
  15. }

初始化代码中只是启动相应的心跳检测,真正创建服务是通过Transporters.bind实现的:

  1. public static Server bind(URL url, ChannelHandler... handlers) throws RemotingException {
  2. .......
  3. return getTransporter().bind(url, handler);
  4. }
  5. public static Transporter getTransporter() {
  6. return ExtensionLoader.getExtensionLoader(Transporter.class).getAdaptiveExtension();
  7. }

这里首先是Transporter$Adpative对象不用在说了吧,而Transporter有三个扩展类,分别是netty、mina、grizzly,未配置默认使用的是netty方法作为底层通信:

  1. public Server bind(URL url, ChannelHandler listener) throws RemotingException {
  2. return new NettyServer(url, listener);
  3. }

可以看到doLocalExport中原来做了这么多事,创建好服务后,接着才会将服务信息(url)注册到Zookeeper:

  1. final Registry registry = getRegistry(originInvoker);
  2. final URL registedProviderUrl = getRegistedProviderUrl(originInvoker);
  3. registry.register(registedProviderUrl);

这段代码流程很清晰,就是拿到一个Registry注册中心对象,然后注册服务,主要看看这里是如何实现的,首先是getRegistry

  1. public static final String REGISTRY_PROTOCOL = "registry";
  2. private Registry getRegistry(final Invoker<?> originInvoker){
  3. URL registryUrl = originInvoker.getUrl();
  4. // 如果是registry://...开头的url,就将其替换为zookeeper://...
  5. if (Constants.REGISTRY_PROTOCOL.equals(registryUrl.getProtocol())) {
  6. String protocol = registryUrl.getParameter(Constants.REGISTRY_KEY, Constants.DEFAULT_DIRECTORY);
  7. registryUrl = registryUrl.setProtocol(protocol).removeParameter(Constants.REGISTRY_KEY);
  8. }
  9. return registryFactory.getRegistry(registryUrl);
  10. }

可以看到是通过registryFactory对象获取具体的注册中心,那这个registryFactory是个啥?

  1. private RegistryFactory registryFactory;
  2. public void setRegistryFactory(RegistryFactory registryFactory) {
  3. this.registryFactory = registryFactory;
  4. }

这个应该就不陌生吧,就是通过injectExtension依赖注入注入的自适应扩展对象RegistryFactory$Adpative

  1. public com.alibaba.dubbo.registry.Registry getRegistry(com.alibaba.dubbo.common.URL arg0) {
  2. if (arg0 == null) throw new IllegalArgumentException("url == null");
  3. com.alibaba.dubbo.common.URL url = arg0;
  4. String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());
  5. if (extName == null)
  6. throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.registry.RegistryFactory) name from url(" + url.toString() + ") use keys([protocol])");
  7. com.alibaba.dubbo.registry.RegistryFactory extension = (com.alibaba.dubbo.registry.RegistryFactory) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.registry.RegistryFactory.class).getExtension(extName);
  8. return extension.getRegistry(arg0);
  9. }

同样的套路,也是基于url的协议获取相应的扩展,那按照这样的逻辑这里最终调用的应该是ZookeeperRegistryFactory.getRegistry方法,但是这个类根本没有这个方法啊。

别着急,我们可以看到这个类是继承了一个抽象类AbstractRegistryFactory,所以就是调用父类的这个方法:

  1. public Registry getRegistry(URL url) {
  2. url = url.setPath(RegistryService.class.getName())
  3. .addParameter(Constants.INTERFACE_KEY, RegistryService.class.getName())
  4. .removeParameters(Constants.EXPORT_KEY, Constants.REFER_KEY);
  5. String key = url.toServiceString();
  6. // 锁定注册中心获取过程,保证注册中心单一实例
  7. LOCK.lock();
  8. try {
  9. Registry registry = REGISTRIES.get(key);
  10. if (registry != null) {
  11. return registry;
  12. }
  13. // 这是一个模板方法,具体的逻辑是由子类实现
  14. registry = createRegistry(url);
  15. if (registry == null) {
  16. throw new IllegalStateException("Can not create registry " + url);
  17. }
  18. REGISTRIES.put(key, registry);
  19. return registry;
  20. } finally {
  21. // 释放锁
  22. LOCK.unlock();
  23. }
  24. }

这样做的好处就是使用模板方法模式抽离公共的代码,而具体创建注册中心逻辑则是由子类自己实现:

  1. public Registry createRegistry(URL url) {
  2. // 这里就不再多说了,就是通过Zookeeper客户端创建连接,而Zookeeper客户端
  3. // 有curator和zkClient两个,默认使用的是zkClient。
  4. return new ZookeeperRegistry(url, zookeeperTransporter);
  5. }

看到这里就明白了getRegistry方法最终返回的是ZookeeperRegistry对象,然后调用register方法注册服务,同样的也是采用模板方法模式实现,所以去父类FailbackRegistry中找:

  1. public void register(URL url) {
  2. // 参数校验
  3. super.register(url);
  4. failedRegistered.remove(url);
  5. failedUnregistered.remove(url);
  6. try {
  7. // 向服务器端发送注册请求
  8. doRegister(url);
  9. } catch (Exception e) {
  10. Throwable t = e;
  11. // 如果开启了启动时检测,则直接抛出异常
  12. boolean check = getUrl().getParameter(Constants.CHECK_KEY, true)
  13. && url.getParameter(Constants.CHECK_KEY, true)
  14. && ! Constants.CONSUMER_PROTOCOL.equals(url.getProtocol());
  15. boolean skipFailback = t instanceof SkipFailbackWrapperException;
  16. if (check || skipFailback) {
  17. if(skipFailback) {
  18. t = t.getCause();
  19. }
  20. throw new IllegalStateException("Failed to register " + url + " to registry " + getUrl().getAddress() + ", cause: " + t.getMessage(), t);
  21. } else {
  22. logger.error("Failed to register " + url + ", waiting for retry, cause: " + t.getMessage(), t);
  23. }
  24. // 将失败的注册请求记录到失败列表,定时重试
  25. failedRegistered.add(url);
  26. }
  27. }
  28. protected void doRegister(URL url) {
  29. try {
  30. zkClient.create(toUrlPath(url), url.getParameter(Constants.DYNAMIC_KEY, true));
  31. } catch (Throwable e) {
  32. throw new RpcException("Failed to register " + url + " to zookeeper " + getUrl() + ", cause: " + e.getMessage(), e);
  33. }
  34. }

最终就是通过zookeeper客户端创建一个节点就完成,至此,整个服务发布流程就结束了。但刚刚还漏掉了一个Invoker没有分析,接下来就详细看看它是个啥!

Invoker分析

Invoker是一个非常重要的模型,在服务端和客户端都会用到它,它的作用可以类比为JDK动态代理中的InvocationHandler并且存储了url、服务接口等信息,可以通过proxyFactory.getInvoker创建。经过前面的学习,我们很容易就能定位到JavassistProxyFactorygetInvoker方法中:

  1. public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {
  2. // TODO Wrapper类不能正确处理带$的类名
  3. final Wrapper wrapper = Wrapper.getWrapper(proxy.getClass().getName().indexOf('$') < 0 ? proxy.getClass() : type);
  4. return new AbstractProxyInvoker<T>(proxy, type, url) {
  5. @Override
  6. protected Object doInvoke(T proxy, String methodName,
  7. Class<?>[] parameterTypes,
  8. Object[] arguments) throws Throwable {
  9. return wrapper.invokeMethod(proxy, methodName, parameterTypes, arguments);
  10. }
  11. };
  12. }

该方法就是创建一个AbstractProxyInvoker的匿名实现类,重写doInvoke并调用wrapper的invokeMethod方法,而doInvoke是在Invoker的invoke方法被调用时触发的,那么invokeMethod是啥?该方法在Wrapper中是一个抽象方法,具体的实现是通过getWrapper -> makeWrapper生成的,这里生成的细节就不详细分析了,主要看看生成的invokeMethod代码:

  1. public Object invokeMethod(Object o,String n,Class[]p,Object[]v)throws java.lang.reflect.InvocationTargetException {
  2. cn.dark.api.IDemoService w;
  3. try{
  4. w=((cn.dark.api.IDemoService)$1);
  5. }catch(Throwable e) {
  6. throw new IllegalArgumentException(e);
  7. }
  8. try{
  9. if("sayHello".equals($2)&&$3.length==1){
  10. return($w)w.sayHello((java.lang.String)$4[0]);
  11. }
  12. }catch(Throwable e){
  13. throw new java.lang.reflect.InvocationTargetException(e);
  14. }
  15. throw new com.alibaba.dubbo.common.bytecode.NoSuchMethodException("Not found method \""+$2+"\" in class cn.dark.api.IDemoService.");
  16. }

没啥好说的,就是去调用具体的服务接口,所以Invoker就相当于是一个代理对象,当客户端发起调用时,就会通过该类转发请求到具体的实现类去。

Dubbo——服务发布原理的更多相关文章

  1. 2、Dubbo源码解析--服务发布原理(Netty服务暴露)

    一.服务发布 - 原理: 首先看Dubbo日志,截取重要部分: 1)暴露本地服务 Export dubbo service com.alibaba.dubbo.demo.DemoService to ...

  2. Dubbo服务发布、引用

    DUBBO原理.应用与面经总结 Dubbo原理和源码解析之服务暴露 Dubbo原理和源码解析之服务引用 服务发布 服务的发布总共做了以下几件事,这个也可以从日志log上看出来: 暴露本地服务 暴露远程 ...

  3. dubbo源码之四——dubbo服务发布

    dubbo版本:2.5.4 服务发布是服务提供方向注册中心注册服务过程,以便服务消费者从注册中心查阅并调用服务. 服务发布方在spring的配置文件中配置如下: <bean id="d ...

  4. 阿里dubbo服务注册原理解析

           阿里分布式服务框架 dubbo现在已成为了外面很多中小型甚至一些大型互联网公司作为服务治理的一个首选或者考虑方案,相信大家在日常工作中或多或少都已经用过或者接触过dubbo了.但是我搜了 ...

  5. 搞懂Dubbo服务发布与服务注册

    一.前言 本文讲服务发布与服务注册,服务提供者本地发布服务,然后向注册中心注册服务,将服务实现类以服务接口的形式提供出去,以便服务消费者从注册中心查阅并调用服务. 本文源码分析基于org.apache ...

  6. dubbo服务暴露原理

    1.发布流程 暴露本地服务 暴露远程服务 启动netty 连接zookeeper 到zookeeper注册 监听zookeeper 2.官方文档 3.看输出日志,就会发现在暴露本地服务之前,有一句很重 ...

  7. 分布式系列 - dubbo服务发布

    单元测试OK,封装为Dubbo服务.   添加依赖 pom.xml   <properties>       <dubbo.version>2.5.3</dubbo.ve ...

  8. 09_dubbo服务发布原理

    [ 启动服务的日志分析 ] 1.暴露本地服务 Export dubbo service com.alibaba.dubbo.demo.DemoService to local registry, du ...

  9. dubbo服务暴露原理-远程暴露

    1.与本地暴露相比,远程暴露也大同小异 我们已经到了第三个关键词Procotol我们来看看他的继承体系图 按照经典图的路线,我们下一个关键词应该就是Server了,从方法名openServer(url ...

随机推荐

  1. spring的动态代理实现

    Host.java package cn.zys.dynamiproxy; public class Host implements Rent{ public void rent(){ System. ...

  2. 杂谈WebApiClient的性能优化

    前言 WebApiClient的netcoreapp版本的开发已接近尾声,最后的进攻方向是性能的压榨,我把我所做性能优化的过程介绍给大家,大家可以依葫芦画瓢,应用到自己的实际项目中,提高程序的性能. ...

  3. [VuePress]个人博客 -- 批处理自动化编译提交 -- 排错记录

    建了一个VuePress的个人博客 想着写个批处理,自动编译并上传到GitHub. 结果遇到两个问题, 一个是,vuepress build docs编译后,这个命令执行完就exit了 研究了下bat ...

  4. C# 数据操作系列 - 18 让Dapper更强的插件

    0. 前言 在前一篇中我们讲到了Dapper的应用,但是给我们的感觉Dapper不像个ORM更像一个IDbConnection的扩展.是的,没错.在实际开发中我们经常用Dapper作为对EF Core ...

  5. Java中的集合(十三) 实现Map接口的Hashtable

    Java中的集合(十三) 实现Map接口的Hashtable 一.Hashtable简介 和HashMap一样,Hashtable采用“拉链法”实现一个哈希表,它存储的内容是键值对(key-value ...

  6. debug PHP程序(xdebug、IntelliJ IDEA)

    之前写PHP程序的都是echo调试,今天感觉太麻烦了就想起研究一下IntelliJ IDEA如何调试PHP程序. 从网上查找了很多资料,大部分都提到在IDE里开启服务,一下就懵了,怎么启这么多服务呢. ...

  7. ASP.NET给图片自动添加水印

    先建一个类,感觉注释已经很详细了,有不懂的欢迎评论 using System; using System.Collections.Generic; using System.Drawing; usin ...

  8. Java实现 蓝桥杯VIP 算法提高 前10名

    算法提高 前10名 时间限制:1.0s 内存限制:256.0MB 问题描述 数据很多,但我们经常只取前几名,比如奥运只取前3名.现在我们有n个数据,请按从大到小的顺序,输出前10个名数据. 输入格式 ...

  9. java实现第三届蓝桥杯提取子串

    提取子串 [代码填空](满分16分) 串"abcba"以字母"c"为中心左右对称:串"abba" 是另一种模式的左右对称.这两种情况我们都称 ...

  10. Java实现第八届蓝桥杯拉马车

    拉马车 题目描述 小的时候,你玩过纸牌游戏吗? 有一种叫做"拉马车"的游戏,规则很简单,却很吸引小朋友. 其规则简述如下: 假设参加游戏的小朋友是A和B,游戏开始的时候,他们得到的 ...