Dubbo源码学习(三)之准备Debug

Oct 1, 2017


Dubbo源码学习(一)之Dubbo Demo中我们了解了dubbo-demo。 在Dubbo源码学习(二)之改造dubbo-demo我们尝试对demo做一点改造,也部署了dubbo-admin。

Log

个人习惯是先从程序的log开始探索框架的基本流程。

dubbo-provider

demo中dubbo-provider默认配置log等级为info,运行后我们可以看到log如:

support.ClassPathXmlApplicationContext: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext
xml.XmlBeanDefinitionReader: Loading XML bean definitions from class path resource [META-INF/spring/dubbo-demo-provider.xml]
logger.LoggerFactory: using logger: com.alibaba.dubbo.common.logger.log4j.Log4jLoggerAdapter
config.AbstractConfig:  [DUBBO] The service ready on spring started. service: com.alibaba.dubbo.demo.DemoService, dubbo version: 2.0.0, current host: 127.0.0.1
config.AbstractConfig:  [DUBBO] Export dubbo service com.alibaba.dubbo.demo.DemoService to local registry, dubbo version: 2.0.0, current host: 127.0.0.1
config.AbstractConfig:  [DUBBO] Export dubbo service com.alibaba.dubbo.demo.DemoService to url dubbo://192.168.0.103:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=86462&side=provider&timestamp=1506999958995, dubbo version: 2.0.0, current host: 127.0.0.1
config.AbstractConfig:  [DUBBO] Register dubbo service com.alibaba.dubbo.demo.DemoService url dubbo://192.168.0.103:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=86462&side=provider&timestamp=1506999958995 to registry registry://224.5.6.7:1234/com.alibaba.dubbo.registry.RegistryService?application=demo-provider&dubbo=2.0.0&pid=86462&registry=multicast&timestamp=1506999938958, dubbo version: 2.0.0, current host: 127.0.0.1
transport.AbstractServer:  [DUBBO] Start NettyServer bind /0.0.0.0:20880, export /192.168.0.103:20880, dubbo version: 2.0.0, current host: 127.0.0.1
multicast.MulticastRegistry:  [DUBBO] Load registry store file:....
multicast.MulticastRegistry:  [DUBBO] Register:....
multicast.MulticastRegistry:  [DUBBO] Send broadcast message: register....
DubboMulticastRegistryReceiver  INFO multicast.MulticastRegistry:  [DUBBO] Receive multicast message: register....
multicast.MulticastRegistry:  [DUBBO] Subscribe: provider://192.168.0.103:20880/com.alibaba.dubbo.demo.DemoService....
DubboMulticastRegistryReceiver  INFO multicast.MulticastRegistry:  [DUBBO] Receive multicast message: subscribe provider://192.168.0.103:20880/com.alibaba.dubbo.demo.DemoService....
multicast.MulticastRegistry:  [DUBBO] Notify urls for subscribe url provider://192.168.0.103:20880/com.alibaba.dubbo.demo.DemoService....

我们先粗略来看下牵涉到的类,从log里获取粗略理解:

  • 首先ClassPathXmlApplicationContext将读取META-INF/spring/dubbo-demo-provider.xml文件。
  • AbstractConfig来获取dubbo相关配置处理dubbo service。
  • AbstractServer来启动一个NettyServer,绑定端口。
  • MulticastRegistry发送多播消息
  • DubboMulticastRegistryReceiver接受多播消息

而后我们根据log具体信息来寻找代码所在地,从第一条AbstractConfig相关信息开始:

  • 关于第一步的疑问,ClassPathXmlApplicationContext是Spring提供的类,其加载配置后如何识别dubbo:application, dubbo:registry, dubbo:protocol, dubbo:service节点呢。 其其实使用了扩展spring schema文件的方式在dubbo-config-spring中定义了一套Spring的Schema扩展。

    在DubboNamespaceHandler类中可以看到,”service”对应的解析器为new DubboBeanDefinitionParser(ServiceBean.class, true):

      public void init() {
        registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(ApplicationConfig.class, true));
        registerBeanDefinitionParser("module", new DubboBeanDefinitionParser(ModuleConfig.class, true));
        registerBeanDefinitionParser("registry", new DubboBeanDefinitionParser(RegistryConfig.class, true));
        registerBeanDefinitionParser("monitor", new DubboBeanDefinitionParser(MonitorConfig.class, true));
        registerBeanDefinitionParser("provider", new DubboBeanDefinitionParser(ProviderConfig.class, true));
        registerBeanDefinitionParser("consumer", new DubboBeanDefinitionParser(ConsumerConfig.class, true));
        registerBeanDefinitionParser("protocol", new DubboBeanDefinitionParser(ProtocolConfig.class, true));
        registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true));
        registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, false));
        registerBeanDefinitionParser("annotation", new DubboBeanDefinitionParser(AnnotationBean.class, true));
      }
    
  • com.alibaba.dubbo.config.spring.ServiceBean里有onApplicationEvent。ServiceBean继承自ServiceConfig并实现了ApplicationListener接口,如果在上下文中部署一个实现了ApplicationListener接口的bean,那么每当在一个ApplicationEvent发布到 ApplicationContext时,这个bean得到通知。其实这就是标准的Obeserver设计模式。

  • onApplicationEvent方法中调用了位于ServiceConfig子类的export()方法。将Bean对象转换URL格式,所有 Bean属性转成URL的参数。

设置log4j.properties里的logger等级log4j.rootLogger=debug, stdout。而后运行Provider来查看更详细的log。

dubbo-consumer

这里就不列出consumer端的log信息了。

Debug

dubbo-demo之deubg

基于dubbo源码,以及上面log信息中获得的一些代码的位置,我们就可以开始我们的debug之旅了。

dubbo-admin之debug

基于Dubbo源码学习(二)之改造dubbo-demo中搭建的环境,我们回到dubbo的源码库中:

  • 源码库中执行mvn tomcat:run
  • IDEA中增加remote debug的配置,端口配置为8000, (IDEA上的remote debug配起来非常方便,只需要修改端口即可,会生成如-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=8000)。
  • 在Providers类的disable方法的第一行上下断点。
  • 在页面上禁用demoService的Provider,将发送GET请求如http://localhost:8888/governance/providers/2/disable
  • 程序将停在断点处

参考资料