一、代码准备

1、示例代码

参考dubbo系列二、dubbo+zookeeper+dubboadmin分布式服务框架搭建(windows平台)

2、简单了解下spring自定义标签

https://www.jianshu.com/p/16b72c10fca8

  1. Spring自定义标签总共可以分为以下几个步骤
  2. 定义Bean 标签解析生成接收配置的POJO
  3. 定义schema文件,定义自定义标签的attr属性
  4. 定义解析类parser,遇到自定义标签如何解析。
  5. 定义命名空间处理类namespaceSupport,遇到自定义的命名标签,能够路由到对应的解析类。
  6. 声明schema,写入spring.schema文件中
  7. 声明自定义标签的命名处理类namespaceHandler,写入spring.handlers文件中

例如dubbo标签:

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
  5. xsi:schemaLocation="http://www.springframework.org/schema/beans
  6. http://www.springframework.org/schema/beans/spring-beans.xsd
  7. http://code.alibabatech.com/schema/dubbo
  8. http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
  9. <!--dubbo应用程序命名-->
  10. <dubbo:application name="dubbo-demo-provider"/>
  11. <!--dubbo注册地址-->
  12. <dubbo:registry address="zookeeper://192.168.1.100:2181"/>
  13. <!--dubbo协议地址-->
  14. <dubbo:protocol name="dubbo" port=""/>
  15. <!--接口声明-->
  16. <dubbo:service interface="com.dubbo.demo.api.DemoRpcService" ref="demoRpcService"/>
  17. <bean id="demoRpcService" class="com.dubbo.demo.DemoRpcServiceImpl"/>
  18. </beans>

3、官网说明

官网:https://dubbo.incubator.apache.org/zh-cn/docs/dev/implementation.html

初始化过程细节

解析服务

基于 dubbo.jar 内的 META-INF/spring.handlers 配置,Spring 在遇到 dubbo 名称空间时,会回调 DubboNamespaceHandler

所有 dubbo 的标签,都统一用 DubboBeanDefinitionParser 进行解析,基于一对一属性映射,将 XML 标签解析为 Bean 对象。

在 ServiceConfig.export() 或 ReferenceConfig.get() 初始化时,将 Bean 对象转换 URL 格式,所有 Bean 属性转成 URL 的参数。

然后将 URL 传给 协议扩展点,基于扩展点的 扩展点自适应机制,根据 URL 的协议头,进行不同协议的服务暴露或引用。

二、dubbo标签解析

1、启动服务,断点跟踪

  1. public static void main(String[] args) throws IOException {
  2. ClassPathXmlApplicationContext context
  3. = new ClassPathXmlApplicationContext("classpath:dubbo-provider.xml");
  4. context.start();
  5. // 阻塞当前进程,否则程序会直接停止
  6. System.in.read();
  7. }

spring启动过程中,随着Spring在初始化过程中,碰到dubbo命名的标签,如(<dubbo:service>,<dubbo:registry>)等标签,会由DubboNamespaceHandler类处理,具体原理见链接Spring自定义标签

DubboNamespaceHandler类源码:

  1. //
  2. // Source code recreated from a .class file by IntelliJ IDEA
  3. // (powered by Fernflower decompiler)
  4. //
  5.  
  6. package com.alibaba.dubbo.config.spring.schema;
  7. 。。。import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
  8.  
  9. public class DubboNamespaceHandler extends NamespaceHandlerSupport {
  10. public DubboNamespaceHandler() {
  11. }
  12.  
  13. public void init() {
         // application标签解析 <dubbo:application name="dubbo-demo-provider"/>
  14. this.registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(ApplicationConfig.class, true));
  15. // module标签解析
    this.registerBeanDefinitionParser("module", new DubboBeanDefinitionParser(ModuleConfig.class, true));
  16.      // module标签解析
         this.registerBeanDefinitionParser("registry", new DubboBeanDefinitionParser(RegistryConfig.class, true));
  17.  
  18.      this.registerBeanDefinitionParser("monitor", new DubboBeanDefinitionParser(MonitorConfig.class, true));
    this.registerBeanDefinitionParser("provider", new DubboBeanDefinitionParser(ProviderConfig.class, true));
  19. this.registerBeanDefinitionParser("consumer", new DubboBeanDefinitionParser(ConsumerConfig.class, true));

  20.      // <dubbo:protocol name="dubbo" port="20880"/>
    this.registerBeanDefinitionParser("protocol", new DubboBeanDefinitionParser(ProtocolConfig.class, true));

  21.      // service标签
         // <dubbo:service interface="com.dubbo.demo.api.DemoRpcService" ref="demoRpcService"/>
        // <bean id="demoRpcService" class="com.dubbo.demo.DemoRpcServiceImpl"/>
    // </beans>
    this.registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true));
  22. this.registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, false));
  23. this.registerBeanDefinitionParser("annotation", new DubboBeanDefinitionParser(AnnotationBean.class, true));
  24. }
  25.  
  26. static {
  27. Version.checkDuplicate(DubboNamespaceHandler.class);
  28. }
  29. }

遇到不同的标签,会由不同的Parser处理,这里重点看服务发布,这行代码:

  1. registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true));

也就是说,当Spring容器处理完<dubbo:service>标签后,会在Spring容器中生成一个ServiceBean ,服务的发布也会在ServiceBean中完成。不妨看一下ServiceBean的定义:

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

2、启动入口类

ServiceBean 同时也是service标签解析之后的bean之一,继承ServiceConfig

该Bean实现了很多接口,关于InitializingBeanDisposableBeanApplicationContextAwareBeanNameAware,这些接口的使用介绍如下链接:

而在Spring初始化完成Bean的组装,会调用InitializingBeanafterPropertiesSet方法,在Spring容器加载完成,会接收到事件ContextRefreshedEvent,调用ApplicationListeneronApplicationEvent方法。

afterPropertiesSet中,和onApplicationEvent中,会调用export(),在export()中,会暴露dubbo服务,具体区别在于是否配置了delay属性,是否延迟暴露,如果delay不为null,或者不为-1时,会在afterPropertiesSet中调用export()暴露dubbo服务,如果为null,或者为-1时,会在Spring容器初始化完成,接收到ContextRefreshedEvent事件,调用onApplicationEvent,暴露dubbo服务。

部分ServiceBean的代码如下:

  1. public class ServiceBean<T> extends ServiceConfig<T> implements InitializingBean, DisposableBean, ApplicationContextAware, ApplicationListener<ContextRefreshedEvent>, BeanNameAware {
  2. //Spring容器初始化完成,调用
  3. public void onApplicationEvent(ContextRefreshedEvent event) {
  4. if (isDelay() && !isExported() && !isUnexported()) {
  5. if (logger.isInfoEnabled()) {
  6. logger.info("The service ready on spring started. service: " + getInterface());
  7. }
  8. //暴露服务
  9. export();
  10. }
  11. }
  12. }

export(),暴露服务过程中,如果发现有delay属性,则延迟delay时间,暴露服务,如果没有,则直接暴露服务。

  1. public synchronized void export() {
  2. //忽略若干行代码
  3. if (delay != null && delay > 0) {
  4. //当delay不为null,且大于0时,延迟delay时间,暴露服务
  5. delayExportExecutor.schedule(new Runnable() {
  6. public void run() {
  7. //暴露服务
  8. doExport();
  9. }
  10. }, delay, TimeUnit.MILLISECONDS);
  11. } else {
  12. //直接暴露服务
  13. doExport();
  14. }
  15. }

而在doExport()中,验证参数,按照不同的Protocol,比如(dubbo,injvm)暴露服务,在不同的zookeeper集群节点上注册自己的服务。

  1. protected synchronized void doExport() {
  2. //忽略10000行代码
  3. doExportUrls();
  4. //忽略10000行代码
  5. }
  6.  
  7. private void doExportUrls() {
  8. List<URL> registryURLs = loadRegistries(true);
  9. for (ProtocolConfig protocolConfig : protocols) {
  10. //按照不同的Protocal暴露服务
  11. doExportUrlsFor1Protocol(protocolConfig, registryURLs);
  12. }
  13. }
  1. // 获取注册中心地址
  2. protected List<URL> loadRegistries(boolean provider) {
  3. checkRegistry();
  4. List<URL> registryList = new ArrayList<URL>();
  5. // protected List<RegistryConfig> registries;解析后的RegistryConfig中获取地址列表
  6. if (registries != null && !registries.isEmpty()) {
  7. for (RegistryConfig config : registries) {
  8. String address = config.getAddress();
  9. if (address == null || address.length() == 0) {
  10. address = Constants.ANYHOST_VALUE;
  11. }
  12. // 如果地址为空,再次从配置文件中取
  13. String sysaddress = System.getProperty("dubbo.registry.address");
  14. if (sysaddress != null && sysaddress.length() > 0) {
  15. address = sysaddress;
  16. }
  17. // 如果地址不为空,拼接协议类型、版本信息
  18. if (address.length() > 0 && !RegistryConfig.NO_AVAILABLE.equalsIgnoreCase(address)) {
  19. Map<String, String> map = new HashMap<String, String>();
  20. appendParameters(map, application);
  21. appendParameters(map, config);
  22. map.put("path", RegistryService.class.getName());
  23. map.put("dubbo", Version.getProtocolVersion());
  24. map.put(Constants.TIMESTAMP_KEY, String.valueOf(System.currentTimeMillis()));
  25. if (ConfigUtils.getPid() > 0) {
  26. map.put(Constants.PID_KEY, String.valueOf(ConfigUtils.getPid()));
  27. }
  28. // 默认dubbo协议
  29. if (!map.containsKey("protocol")) {
  30. if (ExtensionLoader.getExtensionLoader(RegistryFactory.class).hasExtension("remote")) {
  31. map.put("protocol", "remote");
  32. } else {
  33. map.put("protocol", "dubbo");
  34. }
  35. }
  36. // 如果同一个标签配置多个地址,则拆分
  37. List<URL> urls = UrlUtils.parseURLs(address, map);
  38. for (URL url : urls) {
  39. url = url.addParameter(Constants.REGISTRY_KEY, url.getProtocol());
  40. url = url.setProtocol(Constants.REGISTRY_PROTOCOL);
  41. if ((provider && url.getParameter(Constants.REGISTER_KEY, true))
  42. || (!provider && url.getParameter(Constants.SUBSCRIBE_KEY, true))) {
  43. registryList.add(url);
  44. }
  45. }
  46. }
  47. }
  48. }
  49. // 返回格式化后的注册地址
  50. return registryList;
  51. }

3、服务暴露过程

这里以dubbo协议为例,看一下发布的过程,在发布过程中,会用一个变量map保存URL的所有变量和value值,然后调用代理工程proxyFactory,获取代理类,然后将invoker转换成exporter,暴露服务,具体如下:

  1. protocol://host:port/path?key=value&key=value
  1. private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs) {
  2. //如果协议类型为null,则默认为dubbo协议
  3. String name = protocolConfig.getName();
  4. if (name == null || name.length() == 0) {
  5. name = "dubbo";
  6. }
  7. //map是保存url中key-Value的值
  8. Map<String, String> map = new HashMap<String, String>();
  9. //URL中的side属性,有两个值,一个provider,一个consumer,暴露服务的时候为provider
  10. map.put(Constants.SIDE_KEY, Constants.PROVIDER_SIDE);
  11. //dubbo的版本号 url中的dubbo
  12. map.put(Constants.DUBBO_VERSION_KEY, Version.getVersion());
  13. //url中的timestamp
  14. map.put(Constants.TIMESTAMP_KEY, String.valueOf(System.currentTimeMillis()));
  15. //url中的pid
  16. if (ConfigUtils.getPid() > 0) {
  17. map.put(Constants.PID_KEY, String.valueOf(ConfigUtils.getPid()));
  18. }
  19. //从其他参数中获取参数
  20. appendParameters(map, application);
  21. appendParameters(map, module);
  22. appendParameters(map, provider, Constants.DEFAULT_KEY);
  23. appendParameters(map, protocolConfig);
  24. appendParameters(map, this);
  25. //忽略若干代码
  26.  
  27. if (ProtocolUtils.isGeneric(generic)) {
  28. map.put("generic", generic);
  29. map.put("methods", Constants.ANY_VALUE);
  30. } else {
  31. //url中的revesion字段
  32. String revision = Version.getVersion(interfaceClass, version);
  33. if (revision != null && revision.length() > 0) {
  34. map.put("revision", revision);
  35. }
  36. //拼接URL中的methods
  37. String[] methods = Wrapper.getWrapper(interfaceClass).getMethodNames();
  38. if (methods.length == 0) {
  39. logger.warn("NO method found in service interface " + interfaceClass.getName());
  40. map.put("methods", Constants.ANY_VALUE);
  41. } else {
  42. map.put("methods", StringUtils.join(new HashSet<String>(Arrays.asList(methods)), ","));
  43. }
  44. }
  45. //token 临牌校验
  46. if (!ConfigUtils.isEmpty(token)) {
  47. if (ConfigUtils.isDefault(token)) {
  48. map.put("token", UUID.randomUUID().toString());
  49. } else {
  50. map.put("token", token);
  51. }
  52. }
  53. //injvm协议
  54. if ("injvm".equals(protocolConfig.getName())) {
  55. protocolConfig.setRegister(false);
  56. map.put("notify", "false");
  57. }
  58. //获取上下文路径
  59. String contextPath = protocolConfig.getContextpath();
  60. if ((contextPath == null || contextPath.length() == 0) && provider != null) {
  61. contextPath = provider.getContextpath();
  62. }
  63. //获取主机名
  64. String host = this.findConfigedHosts(protocolConfig, registryURLs, map);
  65. //获取端口
  66. Integer port = this.findConfigedPorts(protocolConfig, name, map);
  67. //组装URL,将map中的协议,版本号信息等
  68. URL url = new URL(name, host, port, (contextPath == null || contextPath.length() == 0 ? "" : contextPath + "/") + path, map);
  69. //如果url使用的协议存在扩展,调用对应的扩展来修改原url
  70. if (ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class)
  71. .hasExtension(url.getProtocol())) {
  72. url = ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class)
  73. .getExtension(url.getProtocol()).getConfigurator(url).configure(url);
  74. }
  75.  
  76. String scope = url.getParameter(Constants.SCOPE_KEY);
  77. //配置为none不暴露
  78. if (!Constants.SCOPE_NONE.toString().equalsIgnoreCase(scope)) {
  79. if (!Constants.SCOPE_REMOTE.toString().equalsIgnoreCase(scope)) {
  80. //如果不是remote,则暴露本地服务
  81. exportLocal(url);
  82. }
  83. //如果配置不是local则暴露为远程服务
  84. if (!Constants.SCOPE_LOCAL.toString().equalsIgnoreCase(scope)) {
  85. //忽略日志 如果注册中心地址不为null
  86. if (registryURLs != null && registryURLs.size() > 0) {
  87. for (URL registryURL : registryURLs) {
  88. url = url.addParameterIfAbsent("dynamic", registryURL.getParameter("dynamic"));
  89. //忽略不相干的代码
  90. // 通过代理工厂将ref对象转化成invoker对象
  91. Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString()));
  92. //代理invoker对象
  93. DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);
  94. // 暴露服务
  95. Exporter<?> exporter = protocol.export(wrapperInvoker);
  96. //一个服务可能有多个提供者,保存在一起
  97. exporters.add(exporter);
  98. }
  99. } else {
  100. Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, url);
  101. DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);
  102.  
  103. Exporter<?> exporter = protocol.export(wrapperInvoker);
  104. exporters.add(exporter);
  105. }
  106. }
  107. }
  108. this.urls.add(url);
  109. }

doExportUrlsFor1Protocol代码再简化一下,如下:

  1. private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs) {
  2. Map map=builderUrl();
  3. // 通过代理工厂将ref对象转化成invoker对象
  4. Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, url);
  5. //代理invoker对象
  6. DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);
  7. // 暴露服务
  8. Exporter<?> exporter = protocol.export(wrapperInvoker);
  9. //一个服务可能有多个提供者,保存在一起
  10. exporters.add(exporter);
  11. }

拼接后的url:

  1. dubbo://192.168.1.100:20880/com.dubbo.demo.api.DemoRpcService?anyhost=true&application=dubbo-demo-provider&bind.ip=192.168.1.100&bi
  2.  
  3. nd.port=20880&dubbo=2.6.0&generic=false&interface=com.dubbo.demo.api.DemoRpcService&methods=getUserName&pid=18740&side=provider&timestamp=1538311737815

  1. Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, url);

而在上面proxyFactory.getInvoker中,很显然是获取到的是接口的代理类。

而在 protocol.export(wrapperInvoker)中,将服务暴露出去。

代码如下:

  1. public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
  2. URL url = invoker.getUrl();
  3. //忽略若干代码
  4. //打开服务
  5. openServer(url);
  6. optimizeSerialization(url);
  7. return exporter;
  8. }
  1. private void openServer(URL url) {
  2.  
  3. String key = url.getAddress();
  4. boolean isServer = url.getParameter(Constants.IS_SERVER_KEY, true);
  5. //是否server端
  6. if (isServer) {
  7. ExchangeServer server = serverMap.get(key);
  8. if (server == null) {
  9. //如果服务不存在,创建服务
  10. serverMap.put(key, createServer(url));
  11. } else {
  12. server.reset(url);
  13. }
  14. }
  15. }
  1. private ExchangeServer createServer(URL url) {
  2. //忽略若干代码
  3. ExchangeServer server;
  4. try {
  5. server = Exchangers.bind(url, requestHandler);
  6. } catch (RemotingException e) {
  7. throw new RpcException("Fail to start server(url: " + url + ") " + e.getMessage(), e);
  8. }
  9. return server;
  10. }

而在headerExchangerbind中,调用了Transporters.bind(),一直调用到NettyServer,绑定了端口和链接。

  1. public ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException {
  2. return new HeaderExchangeServer(Transporters.bind(url, new DecodeHandler(new HeaderExchangeHandler(handler))));
  3. }
  4.  
  5. //Transporters.bind
  6. public static Server bind(URL url, ChannelHandler... handlers) throws RemotingException {
  7. //忽略很多代码
  8. return getTransporter().bind(url, handler);
  9. }
  10. //上段代码的getTransporter()
  11. public static Transporter getTransporter() {
  12. return ExtensionLoader.getExtensionLoader(Transporter.class).getAdaptiveExtension();
  13. }

而在Transporter的定义中看到下面代码:

  1. @SPI("netty")
  2. public interface Transporter {
  3. @Adaptive({Constants.SERVER_KEY, Constants.TRANSPORTER_KEY})
  4. Server bind(URL url, ChannelHandler handler) throws RemotingException;
  5. @Adaptive({Constants.CLIENT_KEY, Constants.TRANSPORTER_KEY})
  6. Client connect(URL url, ChannelHandler handler) throws RemotingException;
  7. }

所以这里调用的是NettyTransporter,这里启动了一个新的NettyServer

  1. public class NettyTransporter implements Transporter {
  2.  
  3. public static final String NAME = "netty4";
  4.  
  5. public Server bind(URL url, ChannelHandler listener) throws RemotingException {
  6. return new NettyServer(url, listener);
  7. }
  8.  
  9. public Client connect(URL url, ChannelHandler listener) throws RemotingException {
  10. return new NettyClient(url, listener);
  11. }
  12. }

在NettyServer的构造方法中,调用了父类的构造方法,调用了doOpen()方法指定了端口

  1. public class NettyServer extends AbstractServer implements Server {
  2. public NettyServer(URL url, ChannelHandler handler) throws RemotingException {
  3. super(url, ChannelHandlers.wrap(handler, ExecutorUtil.setThreadName(url, SERVER_THREAD_POOL_NAME)));
  4. }
  5. }
  6.  
  7. public AbstractServer(URL url, ChannelHandler handler) throws RemotingException {
  8. super(url, handler);
  9. //忽略很多代码
  10. doOpen();
  11. //忽略很多代码
  12. }
  13.  
  14. @Override
  15. protected void doOpen() throws Throwable {
  16. NettyHelper.setNettyLoggerFactory();
  17.  
  18. bootstrap = new ServerBootstrap();
  19.  
  20. bossGroup = new NioEventLoopGroup(1, new DefaultThreadFactory("NettyServerBoss", true));
  21. workerGroup = new NioEventLoopGroup(getUrl().getPositiveParameter(Constants.IO_THREADS_KEY, Constants.DEFAULT_IO_THREADS),
  22. new DefaultThreadFactory("NettyServerWorker", true));
  23.  
  24. final NettyServerHandler nettyServerHandler = new NettyServerHandler(getUrl(), this);
  25. channels = nettyServerHandler.getChannels();
  26.  
  27. bootstrap.group(bossGroup, workerGroup)
  28. .channel(NioServerSocketChannel.class)
  29. .childOption(ChannelOption.TCP_NODELAY, Boolean.TRUE)
  30. .childOption(ChannelOption.SO_REUSEADDR, Boolean.TRUE)
  31. .childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
  32. .childHandler(new ChannelInitializer<NioSocketChannel>() {
  33. @Override
  34. protected void initChannel(NioSocketChannel ch) throws Exception {
  35. NettyCodecAdapter adapter = new NettyCodecAdapter(getCodec(), getUrl(), NettyServer.this);
  36. ch.pipeline()//.addLast("logging",new LoggingHandler(LogLevel.INFO))//for debug
  37. .addLast("decoder", adapter.getDecoder())
  38. .addLast("encoder", adapter.getEncoder())
  39. .addLast("handler", nettyServerHandler);
  40. }
  41. });
  42. // bind
  43. ChannelFuture channelFuture = bootstrap.bind(getBindAddress());
  44. channelFuture.syncUninterruptibly();
  45. channel = channelFuture.channel();
  46.  
  47. }

到这里dubbo服务就启动了,但是有一点还是有疑惑,那么,dubbo服务什么时候注册到注册中心的?带着疑惑看了一下官方文档。

也就是说,在调用DubboProtocol暴露服务之前,回去调用拦截器,当发现是regiester,则去注册中心注册服务。

  1. public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
  2. //如果是registerProtocol,则调用RegisterProtocol.export方法
  3. if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {
  4. return protocol.export(invoker);
  5. }
  6. return new ListenerExporterWrapper<T>(protocol.export(invoker),
  7. Collections.unmodifiableList(ExtensionLoader.getExtensionLoader(ExporterListener.class)
  8. .getActivateExtension(invoker.getUrl(), Constants.EXPORTER_LISTENER_KEY)));
  9. }

而在RegisterProtocol.export

  1. public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {
  2. //export invoker
  3. final ExporterChangeableWrapper<T> exporter = doLocalExport(originInvoker);
  4.  
  5. URL registryUrl = getRegistryUrl(originInvoker);
  6.  
  7. //根据SPI机制获取具体的Registry实例,这里获取到的是ZookeeperRegistry
  8. final Registry registry = getRegistry(originInvoker);
  9. final URL registedProviderUrl = getRegistedProviderUrl(originInvoker);
  10. boolean register = registedProviderUrl.getParameter("register", true);
  11. if (register) {
  12. //在这里注册服务
  13. register(registryUrl, registedProviderUrl);
  14. //忽略很多代码
  15. }
  16. //忽略很多代码
  17. }
  18.  
  19. public void register(URL registryUrl, URL registedProviderUrl) {
  20. Registry registry = registryFactory.getRegistry(registryUrl);
  21. registry.register(registedProviderUrl);
  22. }

ZookeeperRegistry继承父类FailbackRegistry,在父类的register方法中,调用了 doRegister,doRegister中,创建了ZK节点,这样就将自己的服务暴露到注册中心zk上:

  1. @Override
  2. public void register(URL url) {
  3. //忽略很多代码
  4. doRegister(url);
  5. //忽略很多代码
  6. }
  7.  
  8. protected void doRegister(URL url) {
  9. try {
  10. zkClient.create(toUrlPath(url), url.getParameter(Constants.DYNAMIC_KEY, true));
  11. } catch (Throwable e) {
  12. throw new RpcException("Failed to register " + url + " to zookeeper " + getUrl() + ", cause: " + e.getMessage(), e);
  13. }
  14. }

这样,整个dubbo服务就启动了。再回头看官方文档上的说明,就很清楚了。

三、类调用关系

1、provider提供方

ClassPathXmlApplicationContext <init>(构造方法)
-> ClassPathXmlApplicationContext refresh()
-> ClassPathXmlApplicationContext finishRefresh()
-> AbstractApplicationContext publishEvent()
-> ServiceBean onApplicationEvent()
-> ServiceConfig doExport()
#构造dubbo对象 application provider module protocol registry service reference consume等
 
-> ServiceConfig doExportUrls #导出URL,获取注册中心RegistryConfig
#注册中心:registry://10.199.101.228:2181/com.alibaba.dubbo.registry.RegistryService?application=demo&backup=10.199.101.227:2181,10.199.101.229:2181&dubbo=2.4.9&pid=8045&registry=zookeeper&timestamp=1491546077803
 
-> ServiceConfig doExportUrlsFor1Protocol()
#需要暴露 dubbo://10.199.66.242:20880/com.unj.dubbotest.provider.DemoService?anyhost=true&application=dubbo_demo_provider&dubbo=2.4.9&interface=com.unj.dubbotest.provider.DemoService&methods=sayHello,getUsers&pid=8045&revision=0.0.1&side=provider&timestamp=1491546674441&version=0.0.1
 
-> ServiceConfig exportLocal()
-> Exporter<?> exporter = protocol.export(proxyFactory.getInvoker(ref, (Class) interfaceClass, local));
#暴露Invoker<XxxService>调用服务代理类
 
-> proxyFactory.getInvoker(ref, (Class) interfaceClass, local)
#返回 AbstractProxyInvoker代理ProxyInvoker<XxxService>
public abstract class AbstractProxyInvoker<T> implements Invoker<T> {
private final T proxy; //代理目标实例 XxxServiceImpl
private final Class<T> type;
private final URL url;
}
-> InvokerInvocationHandler.invoke()
#invoker.invoke(new RpcInvocation(method, args)).recreate();
 
-> DubboProtocol export(Invoker<T> invoker)
# 返回暴露Exporter<T>
public class DubboExporter<T> extends AbstractExporter<T> {
private final String key; //com.unj.dubbotest.provider.DemoService:0.0.1:20880
private final Map<String, Exporter<?>> exporterMap;
public DubboExporter(Invoker<T> invoker, String key, Map<String, Exporter<?>> exporterMap){
super(invoker);
this.key = key;
this.exporterMap = exporterMap;
}
 
-> DubboProtocol openServer(url)
#url dubbo://10.199.66.242:20880/com.unj.dubbotest.provider.DemoService?anyhost=true&application=dubbo_demo&dubbo=2.4.9&interface=com.unj.dubbotest.provider.DemoService&methods=sayHello,getUsers&pid=8045&revision=0.0.1&side=provider&timestamp=1491546674441&version=0.0.
#serverMap.put(key, createServer(url)); key:10.199.66.242:20880 value:ExchangeServer
 
-> DubboProtocol createServer(URL url)
#返回HeaderExchangeServer,添加参数列表 如心跳,心跳时间
-> Exchangers.bind(url, requestHandler);
#返回HeaderExchangeServer,getTransporter()获取的实例来源于配置,默认返回一个NettyTransporter
-> HeaderExchangeServer.bind(URL url, ExchangeHandler handler);
 
-> HeaderExchangeServer(Transporters.bind(url, new DecodeHandler(new HeaderExchangeHandler(handler))));
#HeaderExchangeServer包装实例NettyServer
 
-> NettyTransporter.bind(URL url, ChannelHandler listener)
#return new NettyServer(url, listener)
 
-> NettyServer.doOpen();
#打开socket监听端口准备接收消息
#ServerBootstrap bind(getBindAddress())绑定地址端口
#RpcInvocation 具体类名、方法名、调用参数
#DubboInvoker – 执行具体的远程调用,包含初始化信息如client
#Protocol – 服务地址的发布和订阅
#Exporter – 暴露服务的引用,或取消暴露

2、consume(消费方)

->ReferenceConfig.init
#consume端启动初始化
->DubboProtocol.refer
#根据参数url,接口等构建Invoker
->JavassistProxyFactory.getProxy(Invoker<T> invoker, Class<?>[] interfaces)
#构建代理对象Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));
 
->DemoService.say(String hello);#真正调用时候
->InvokerInvocationHandler.invoke(Object proxy, Method method, Object[] args)
#invoker.invoke(new RpcInvocation(method, args)).recreate();RpcInvocation包装参数方法名
->DubboInvoker.doInovke(final Invocation invocation)
->MockClusterInvoker是否Mock
->FailfastClusterInvoker.invoke
->RegistryDirectory的methodInvokerMap 获取invock列表
->Loadbalance RegistryDirectory.doList(Invocation)负载均衡
 
#统一代理调用
->ExchangeClient.send(invocation, isSent);
->HeaderExchangeChannel.request(Object request, int timeout)
->NettyChannel.send(Object message, boolean sent) 
 
 

3、dubbo 底层通讯

NettyClient <-- 异步NIO传输 socket监听-> NettyServer
 

4、consume --> provider 调用过程

 
-> NettyServer->NettyHandler.messageReceived #接收消息处理器
-> MultiMessageHandler->HeartbeatHandler->AllChannelHandler->DecodeHandler->HeaderExchangeHandler->DubboProtocol$requestHandler
#NettyServer启动时候绑定MultiMessageHandler
#DubboProtocol.getServers() 检索serverMap获取Exporter<?>
#DubboProtocol.getServers() 检索serverMap获取ExchangeServer
-> ExchangeHandlerAdapter.reply
#真正获取Invoker,将传入message 转换 invocation
-> invoker.invoke(invocation)
-> JavassistProxyFactory$AbstractProxyInvoker.doInvoke
#服务端Invoker代理 AbstractProxyInvoker调用目标引用service,客户端DubboInvoker

dubbo系列四、dubbo服务暴露过程源码解析的更多相关文章

  1. Dubbo服务调用过程源码解析④

    目录 0.服务的调用 1.发送请求 2.请求编码 3.请求的解码 4.调用具体服务 5.返回调用结果 6.接收调用结果 Dubbo SPI源码解析① Dubbo服务暴露源码解析② Dubbo服务引用源 ...

  2. (转)Dubbo服务暴露过程源码分析

    参考

  3. Dubbo消费方服务调用过程源码分析

    参考:dubbo消费方服务调用过程源码分析dubbo基于spring的构建分析Dubbo概述--调用过程dubbo 请求调用过程分析dubbo集群容错机制代码分析1dubbo集群容错策略的代码分析2d ...

  4. 【图解源码】Zookeeper3.7源码分析,包含服务启动流程源码、网络通信源码、RequestProcessor处理请求源码

    Zookeeper3.7源码剖析 能力目标 能基于Maven导入最新版Zookeeper源码 能说出Zookeeper单机启动流程 理解Zookeeper默认通信中4个线程的作用 掌握Zookeepe ...

  5. Spring IOC容器启动流程源码解析(四)——初始化单实例bean阶段

    目录 1. 引言 2. 初始化bean的入口 3 尝试从当前容器及其父容器的缓存中获取bean 3.1 获取真正的beanName 3.2 尝试从当前容器的缓存中获取bean 3.3 从父容器中查找b ...

  6. Java 集合系列05之 LinkedList详细介绍(源码解析)和使用示例

    概要  前面,我们已经学习了ArrayList,并了解了fail-fast机制.这一章我们接着学习List的实现类——LinkedList.和学习ArrayList一样,接下来呢,我们先对Linked ...

  7. Java 集合系列07之 Stack详细介绍(源码解析)和使用示例

    概要 学完Vector了之后,接下来我们开始学习Stack.Stack很简单,它继承于Vector.学习方式还是和之前一样,先对Stack有个整体认识,然后再学习它的源码:最后再通过实例来学会使用它. ...

  8. Java 集合系列10之 HashMap详细介绍(源码解析)和使用示例

    概要 这一章,我们对HashMap进行学习.我们先对HashMap有个整体认识,然后再学习它的源码,最后再通过实例来学会使用HashMap.内容包括:第1部分 HashMap介绍第2部分 HashMa ...

  9. Java 集合系列11之 Hashtable详细介绍(源码解析)和使用示例

    概要 前一章,我们学习了HashMap.这一章,我们对Hashtable进行学习.我们先对Hashtable有个整体认识,然后再学习它的源码,最后再通过实例来学会使用Hashtable.第1部分 Ha ...

随机推荐

  1. suoi07 区间平均++ (二分答案+前缀和)

    https://www.vijos.org/d/SUOI/p/59dc5af7d3d8a1361ae62b97 二分一个答案,然后做一做前缀和,用满足区间大小的最小值减一减,判断答案合不合法 然而还要 ...

  2. SimpleDateFormat是线程不安全的,切忌切忌!

    多线程方法中使用了共享变量SimpleDateFormat,报如下错误: java.lang.NumberFormatException: multiple points  at sun.misc.F ...

  3. 常用数据结构及算法C#/Java实现

    常用数据结构及算法C#实现 1.冒泡排序.选择排序.插入排序(三种简单非递归排序) ,, , , , , , , , , }; //冒泡排序 int length = waitSort.Length; ...

  4. Spring的后置处理器BeanFactoryPostProcessor

    新建一个JavaBean UserBeanFactoryPostProcessor 实现了BeanFactoryPostProcessor接口 Spring配置文件如下: 编写测试用例 从结果可以看出 ...

  5. plot与legend画图与图例

    画图与图例: legend(x, y = NULL, legend, fill = NULL, col = par("col"), border = "black&quo ...

  6. Golang面向API编程-interface(接口)

    Golang面向API编程-interface(接口) 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. Golang并不是一种典型的面向对象编程(Object Oriented Pr ...

  7. 使用swagger来编写在线api文档

    swagger是一个非常简单,强大的框架.快速上手,只需要引入jar包 , 使用注解就可以生成一个漂亮的在线api文档 pom.xml <dependency> <groupId&g ...

  8. Hbase记录-HBase客户端API

    本章介绍用于对HBase表上执行CRUD操作的HBase Java客户端API. HBase是用Java编写的,并具有Java原生API.因此,它提供了编程访问数据操纵语言(DML). HBaseCo ...

  9. Hbase记录-Hbase其他工具

    1.RowCounter工具可以查看某张表有多少行,效率非常高 2.count命令在数据量大的时候效率非常差 执行./hbase  org.apache.hadoop.habse.mapreduce. ...

  10. layui(一)——layDate组件常见用法

    和 layer 一样,我们可以在 layui 中使用 layDate,也可直接使用 layDate 独立版,可按照实际需求来选择.options整理如下: layui.use('laydate', f ...