dubbo源码详细分析

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/feivirus/article/details/80211121
一.总体
(一).分层架构:http://dubbo.apache.org/books/dubbo-dev-book/design.html
核心 registry(注册消费者)->cluster(集群处理)->dubbo.rpc(代理封装格式)->remoting(远程网络传输)
详细的从上到下依次为service,config,proxy,registry,cluster,monitor,protocol,,exchange,transport,
serialize模块
(二).采用Microkernel + Plugin模式,Microkernel只负责组装Plugin,Dubbo自身的功能也是通过扩展点实现.
在扩展类的jar包内,放置扩展点配置文件:META-INF/dubbo/接口全限定名,内容为:配置名=扩展实现类全限定名,多个实现类用换行符分隔.
(三).生产者和spring结合
spring加载dubbo过程。
由于spring实例化的ServiceBean是单例模式的,在Spring的容器ApplicationContext的启动过程refresh过程中最后第二步会预先初始化单例的bean, 在bean的初始化过程会设置beanName,设置容器applicationContext,回调InitializingBean的afterPropertiesSet。最后一步finishRefresh会触发ContextRefreshedEvent事件, 而ServiceBean实现了ApplicationListener接口监听了此事件, 而在之前一步实例化的ServiceBean注册了这个事件,所以ServiceBean的onApplicationEvent(ApplicationEvent event)方法被触发, 在这个方法中触发了export方法来暴露服务。
(四).领域概念
1.服务提供端:ServiceConfig->ProxyFactory(JavassistProxyFactory或者JdkProxyFactory)->Invoker(AbstractProxyInvoker的实例)->filter(exception,monitor等)->RegistryProtocol(Dubbo,Hessian,Injvm,Rmi,WebService等)->exporter->server->transporter->serializtion->codec(telnet,transport,exchange)
2.消费调用端:jdk dynamic proxy->cluster->directory->registry->router->loadbalance->filter(monitor等)->invoker->client->transporter->codec

二.服务端代码流程
(二.0)先在导出服务的service的bean配置中注入ProviderConfig(threadPool=cached),
ApplicationConfig(dubbo.application.environment=test/develop/product, dubbo.application.provider.name=linglongta.test.provider, dubbo.monitor.isNeed=notNeed),
MonitorConfig(dubbo.monitor.address.port=10.168.163.241:7070),
RegistryConfig(dubbo.registry.address=10.168.163.241,dubbo.registry.port=2811,dubbo.registry.protocol=zookeeper),
ProtocolConfig(dubbo.application.protocol=dubbo,dubbo.protocol.port=-1).
在每个接口中,设置interface,group(dubbo.provider.group=testLingLongTaGroup),application(前面的ApplicationConfig),protocol(前面的ProtocolConfig),
registry(前面的RegistryConfig),ref(注入的service实现),timeout(dubbo.provider.timeout=300000).
(二.1)Config层
1.由spring进入com.alibaba.dubbo.config.ServiceConfig.export()(消费端的有一个类似的ReferenceConfig类)函数,获取前面ProviderConfig的export属性的布尔值,判断是否暴露,不暴露的话直接返回。获取前面ProviderConfig的delay属性的整数值,如果需要delay,则delay几秒在调用doExport.否则,立即调用doExport.进入函数com.alibaba.dubbo.config.ServiceConfig.doExport(),
com.alibaba.dubbo.config.ServiceConfig.doExport()函数过程:
1.1设置exported为true,防止重复进入导出函数导出这个服务。判断interfaceName是否为空,为空抛异常。怀疑interfaceName是ServiceConfig的构造函数成通过模板com.alibaba.dubbo.config.annotation.Service设置的??
1.2进入函数com.alibaba.dubbo.config.AbstractConfig.appendProperties(AbstractConfig),把上面的ProviderConfig调用方法com.alibaba.dubbo.config.AbstractConfig.appendProperties(AbstractConfig)复制到属性ServiceConfig.provider中,具体逻辑是读取provider中的xml中的配置,比如AbstractConfig可能未registry,protocol,application中的一种,设置到属性中.
1.3设置provider的ApplicationConfig为ServiceConfig的application属性,设置provider的ModuleConfig为ServiceConfig的module属性,设置provider的List的RegistryConfig为ServiceConfig的registries属性,设置provider的MonitorConfig为ServiceConfig的monitor属性,设置provider的list的ProtocolConfig为ServiceConfig的protocols属性,
1.4如果dubbo接口的实现类ref属于com.alibaba.dubbo.rpc.service.GenericService类型,代表泛化调用,设置为interfaceClass为GenericService类型。否则,通过反射设置interfaceClass为调用dubbo的interface接口类型.检查配置的MethodConfig中的方法是否都在该interface中存在,不存在则报异常。调用com.alibaba.dubbo.config.ServiceConfig.checkRef()检查dubbo的bean对象必须实现了bean的interface接口.如果服务接口的本地实现类名stub不为空,检查它是否实现或者继承了接口.
1.5.做dubbo的旧版本ApplicationConfig兼容。做dubbo的旧版本RegistryConfig兼容。做dubbo的旧版本ProtocolConfig兼容。
2.调用com.alibaba.dubbo.config.ServiceConfig.doExportUrls()方法导出URL接口。
2.1调用com.alibaba.dubbo.config.AbstractInterfaceConfig.loadRegistries(boolean)遍历所有的RegistryConfig,目前已知的注册中心只有zookeeper好像。每次遍历时,获取注册中心的ip地址列表,path,dubbo版本,时间戳,protocol,将这些变量put到map中,调用com.alibaba.dubbo.common.utils.UrlUtils.parseURLs()根据ip地址的个数转成对应个数的URL字串的列表。
2.2循环遍历不同的ProtocolConfig,比如dubbo,rmi,http,hessian,injvm等,目前基本都是dubbo.进入函数com.alibaba.dubbo.config.ServiceConfig.doExportUrlsFor1Protocol(ProtocolConfig, List<URL>).该函数逻辑:连接所有的地址列表,看是否可以连接。建立map,放入dubbo的version,时间戳,application,module,protocolConfig,interface,methods,ip等,这些可以用来生产URL.遍历所有方法配置,看参数类型是否匹配?根据配置,导出URL的服务。
2.3 遍历所有地址列表,配置的了monitor加载monitor,并给URL设置MONITOR_KEY.根据服务具体实现,实现接口以及regitryUrl从代理工厂ProxyFactory获取代理Invoker(继承于AbstractProxyInvoker),通过方法com.alibaba.dubbo.rpc.ProxyFactory.getInvoker(T, Class<T>, URL)实现,它是对具体实现的一种代理.默认使用javassist库做反射.此处重要!!!进入com.alibaba.dubbo.rpc.proxy.javassist.JavassistProxyFactory.getInvoker()获取Invoker.只是获取新的Invoker,不会调用它的com.alibaba.dubbo.common.bytecode.Wrapper.invokeMethod()方法.
2.4调用com.alibaba.dubbo.rpc.Protocol.export(Invoker<?>)暴露出RegistryProtocol的export()函数。
(二.2)RegistryProtocol层(以dubboProtocol为例,有此层存在的目的是还有dubbo,hessian,memche,redis,thrift等协议)
进入com.alibaba.dubbo.registry.integration.RegistryProtocol.export(Invoker<T>).这步会调用com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper.buildInvokerChain()建立Filter链. 在此方法中调用com.alibaba.dubbo.registry.RegistryService.register(),进入com.alibaba.dubbo.registry.zookeeper.ZookeeperRegistry.ZookeeperRegistry()创建zookeeper服务节点。在com.alibaba.dubbo.registry.support.AbstractRegistry.AbstractRegistry(URL)会缓存文件,服务提供方的地址.然后调用函数com.alibaba.dubbo.registry.integration.RegistryProtocol.doLocalExport()暴露服务。步骤是进入函数com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol.export(Invoker<T>).根据invoker的url生成cacheKey(格式为com.alibaba.dubbo.demo.DemoService:20880).根据invoker,cacheKey,exporterMap生成DubboExporter,主要是把前面的三个字段作为exporter的属性存起来.从URL中判断是否是代理支持的事件,是否是回调服务。调用com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol.openServer(URL)方法根据Url创建一个Server,serverMap的key格式为(192.168.2.242:20880).默认使用netty,dubbo协议,我理解加入Exchanger的目的是除了Netty,可以扩展为其他网络框架,比如支持http,mina,grizzly,p2p,netty4,zookeeper等,类似mvc的分层隔离。然后依次进入dubbo-remoting层的com.alibaba.dubbo.remoting.exchange.Exchangers.bind(),com.alibaba.dubbo.remoting.exchange.support.header.HeaderExchanger.bind(),com.alibaba.dubbo.remoting.transport.netty.NettyTransporter.bind().加入Transporter的目的是区分Grizzly,Netty,Mina等的NIO框架.出入的回调处理类为com.alibaba.dubbo.remoting.transport.DecodeHandler.dubbo在暴露服务最终开启的netty服务监听,监听消费者发送的请求,通过反射调用方法得到结果通过 tcp/ip 网络传输返回给消费者。在com.alibaba.dubbo.remoting.transport.netty.NettyServer.NettyServer()的构造方法中,进入方法com.alibaba.dubbo.remoting.transport.netty.NettyServer.doOpen()开启服务器。
1.Netty监听.进入函数com.alibaba.dubbo.remoting.transport.AbstractServer.AbstractServer(URL, ChannelHandler)->com.alibaba.dubbo.remoting.transport.netty.NettyServer.doOpen().创建server监听.具体发送,接受消息报文的方法在com.alibaba.dubbo.remoting.transport.netty.NettyHandler类中。
2. 如前所述,开启netty服务是在RegistryProtocol.export 的doLocalExport中,在开启了netty服务后.在RegistryProtocol.export中调用com.alibaba.dubbo.registry.RegistryService.subscribe(URL, NotifyListener)进入com.alibaba.dubbo.registry.zookeeper.ZookeeperRegistry.doSubscribe(URL, NotifyListener)方法在zookeeper上注册服务节点了,消费者在消费服务时会根据消费的接口名找到对应的zookeeper节点目录,对目录进行监听,接收推送.常用注册中心实现有 dubbo、Redis、zookeeper,这也是在我们配置时经常看到的 注册协议的配置 ,最为常用的就是 zookeeper.在zookeeper上面创建节点,默认不分组的情况下,服务结构如下:provider://192.168.2.242:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&category=configurators&check=false&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=8132&side=provider&timestamp=1525425383005.当zk上节点变化时进入com.alibaba.dubbo.registry.support.FailbackRegistry.notify(URL, NotifyListener, List<URL>).还会注册/dubbo/com.alibaba.dubbo.demo.DemoService/(configurators, consumers, providers, routers)节点.

三.消费端代码流程
(三.1)消费端初始化(主要是根据xml配置生成url,再生成invoker对象)
消费者初始化时主要做的事情就是引用对应的远程服务,执行了以下步骤:监听注册中心,连接服务提供端,创建消费端服务代理.
比如飞侠中的cn.shibei.feixia.remote.dubbo.ShibeiRemoteServiceConfig这种初始化消费者的方式。流程是.
1.调用org.springframework.beans.factory.support.AbstractBeanFactory.getBean(),进入com.alibaba.dubbo.config.ReferenceConfig.get(),进入com.alibaba.dubbo.config.ReferenceConfig.init()(服务端是ServiceConfig)初始化主要目的是读取xml中的dubbo相关配置放置到ReferenceConfig的属性中,放置到一个map中.步骤是:通过反射生成interface的接口对象,比如interface com.alibaba.dubbo.demo.DemoService.进入com.alibaba.dubbo.config.ReferenceConfig.createProxy(Map),参数是前面的map,内容比如{side=consumer, application=demo-consumer, register.ip=192.168.11.190, methods=sayHello, qos.port=33333, dubbo=2.0.0, pid=3100, check=false, interface=com.alibaba.dubbo.demo.DemoService, timestamp=1525500374971}.先调用com.alibaba.dubbo.config.AbstractInterfaceConfig.loadRegistries()根据xml中的注册中心的配置,比如[<dubbo:registry address="127.0.0.1:4182,127.0.0.1:4180,127.0.0.1:4181" protocol="zookeeper" id="com.alibaba.dubbo.config.RegistryConfig" />]生成消费端的URL对象,比如[registry://127.0.0.1:4182/com.alibaba.dubbo.registry.RegistryService?application=demo-consumer&backup=127.0.0.1:4180,127.0.0.1:4181&dubbo=2.0.0&pid=6676&qos.port=33333&registry=zookeeper&timestamp=1525502765902].
2.在createProxy中然后依次调用ProtocolListenerWrapper->ProtocolFilterWrapper->RegistryProtocol实例的refer方法->com.alibaba.dubbo.registry.support.AbstractRegistryFactory.getRegistry(URL),URL格式为zookeeper://127.0.0.1:4182/com.alibaba.dubbo.registry.RegistryService?application=demo-consumer&backup=127.0.0.1:4180,127.0.0.1:4181&dubbo=2.0.0&interface=com.alibaba.dubbo.registry.RegistryService&pid=6676&qos.port=33333&timestamp=1525502765902.然后调用com.alibaba.dubbo.registry.zookeeper.ZookeeperRegistryFactory.createRegistry(URL), new 一个ZookeeperRegistry()对象返回,在构造方法中初始化zk连接,调用zkClient连接zookeeper://127.0.0.1:4182/com.alibaba.dubbo.registry.RegistryService?application=demo-consumer&backup=127.0.0.1:4180,127.0.0.1:4181&dubbo=2.0.0&interface=com.alibaba.dubbo.registry.RegistryService&pid=6676&qos.port=33333&timestamp=1525502765902.
3.在RegistryProtocol.refer(Class<T>, URL)中继续调用com.alibaba.dubbo.registry.integration.RegistryProtocol.doRefer(Cluster, Registry, Class<T>, URL).
在方法中new一个RegistryDirectory对象,属性为Registry,Protocol?在方法中调用com.alibaba.dubbo.registry.support.FailbackRegistry.register(URL),参数是consumer://192.168.11.190/com.alibaba.dubbo.demo.DemoService?application=demo-consumer&category=consumers&check=false&dubbo=2.0.0&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=6676&qos.port=33333&side=consumer&timestamp=1525502466637.调用com.alibaba.dubbo.registry.zookeeper.ZookeeperRegistry.doRegister(URL),调用zkClient.create把消费者注册到注册中心,注册的节点为/dubbo/com.alibaba.dubbo.demo.DemoService/consumers/consumer%3A%2F%2F192.168.11.190%2Fcom.alibaba.dubbo.demo.DemoService%3Fapplication%3Ddemo-consumer%26category%3Dconsumers%26check%3Dfalse%26dubbo%3D2.0.0%26interface%3Dcom.alibaba.dubbo.demo.DemoService%26methods%3DsayHello%26pid%3D6676%26qos.port%3D33333%26side%3Dconsumer%26timestamp%3D1525502466637.存储了服务消费方ip、group、接口名称、版本、应用名称等.消费端本地会缓存远程服务提供者(每个提供者对应一个Invoker对象)、注册中心配置、路由配置信息。监听注册中心路径是/dubbo/interfaceClass/providers和/dubbo/{interfaceClass}/configurators, /dubbo/${interfaceClass}/routers的节点,当提供者、配置、路由信息发生变化之后注册中心会通知消费者刷新本地缓存。Dubbo框架通过在消费端缓存提供者的信息消除对注册中心的强依赖,即使注册中心挂了服务依然可用。
4.在RegistryProtocol.doRefer()中注册完后,调用com.alibaba.dubbo.registry.support.FailbackRegistry.subscribe(URL, NotifyListener)进入com.alibaba.dubbo.registry.zookeeper.ZookeeperRegistry.doSubscribe(URL, NotifyListener)监听注册中心的节点变化,注册的节点类似服务端,有[dubbo://192.168.11.190: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=3456&side=provider&timestamp=1525499817390, empty://192.168.11.190/com.alibaba.dubbo.demo.DemoService?application=demo-consumer&category=configurators&check=false&dubbo=2.0.0&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=6676&qos.port=33333&side=consumer&timestamp=1525502466637, empty://192.168.11.190/com.alibaba.dubbo.demo.DemoService?application=demo-consumer&category=routers&check=false&dubbo=2.0.0&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=6676&qos.port=33333&side=consumer&timestamp=1525502466637]三个,主要是provider地址,configurators,routers三个.
5.在com.alibaba.dubbo.registry.integration.RegistryDirectory.subscribe(URL)中进入com.alibaba.dubbo.registry.support.FailbackRegistry.notify(),调用com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol.refer()创建DubboInvoker对象.属性是ExchangeClient,version,invokers,生成的ExchangeClient的数量根据service_share_connect判断,默认是true,即调用com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol.getSharedClient(URL)只生成一个连接.调用com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper.buildInvokerChain()生成Filter链,默认有三个[ConsumerContextFilter, FutureFilter, MonitorFilter].我理解是为了用于DubboMonitor对每次调用进行监控。类似spring中的aop.
6.RegistryProtocol.doRefer方法的最后,调用com.alibaba.dubbo.rpc.cluster.Cluster.join()?由Cluster组件来创建一个Invoker并返回,默认是com.alibaba.dubbo.rpc.cluster.support.FailoverCluster.join(Directory<T>),FailoverCluster的directory属性引用上面创建的RegistryDirectory对象。返回生成的Invoker实例之后,由ProxyFactory生成一个持有该Invoker实例的代理,代理回调时会激活该Invoker调用它的invoke方法.进入com.alibaba.dubbo.rpc.proxy.javassist.JavassistProxyFactory.getInvoker()调用doInvoke.

四.远程服务调用流程.
消费端调用提供端服务的过程要执行下面几个步骤: 
a. 消费端触发请求 
b. 消费端请求编码 
c. 提供端请求解码 
d. 提供端处理请求 
e. 提供端响应结果编码 
f. 消费端响应结果解码
(四.1)消费端触发请求
1.在消费者初始化的时候,会生成一个消费者代理(jdk的动态代理)注册到容器中,该代理回调中持有一个MockClusterInvoker实例,消费调用服务接口时它的invoke会被调用.调用需要的interface后,进入代理对象的对应方法中,然后统一进入com.alibaba.dubbo.rpc.proxy.InvokerInvocationHandler.invoke().如果是toString(),hashCode(),equals()方法,直接进入MockClusterInvoker对象的相应方法.此时调用com.alibaba.dubbo.rpc.cluster.support.wrapper.MockClusterInvoker.invoke(Invocation),参数为new出一个RpcInvocation对象.RpcInvocation对象的属性为调用的interface的方法名,参数类型,参数,invoker对象.在这个invoke方法中,判断请求是否需要mock,是否配置了mock属性,是force mock(消费方的调用直接返回null,不发起远程调用,用来屏蔽不重要的应用对服务端的影响)还是fail mock(调用失败返回null,不抛异常,用于服务不稳定时对服务端的影响).默认mock为false,主要用于服务降级,对应dubbo monitor中的动态配置.进入 com.alibaba.dubbo.rpc.cluster.support.FailoverClusterInvoker.doInvoke().常用的cluster集群容错架构还有Failfast Cluster(快速失败,只发起一次调用,失败立即报错,用于非幂等性操作,比如网络情况不好,写操作),Failsafe(失败安全,报异常时直接忽略,用于写入日志等),Failback Cluster(失败自动恢复),
Forking Cluster(并行调用多个,一个成功就返回),Broadcst Cluster(广播调用,任何一个报错则报错,比如更新缓存等). cluster主要将directory中的多个invoker封装成一个,区分不同的失败重试策略.默认的FailoverCluster是失败自动切换,当出现失败,重试其它服务器,默认重试3次.
2.FailoverClusterInvoker.doInvoke()中。先调用checkInvokers()检查对应的invokers是否为空,为空则抛出没有provider的异常日志.然后根据retries次数的配置得到dubbo的尝试调用次数,默认是3次。如果是第一次调用直接进入com.alibaba.dubbo.rpc.cluster.support.AbstractClusterInvoker.select()根据负载均衡策略选择一个invoker调用.如果是第二次开始要先进入com.alibaba.dubbo.rpc.cluster.support.AbstractClusterInvoker.list()(多态实际是FailoverClusterInvoker),进入com.alibaba.dubbo.registry.integration.RegistryDirectory.List(Invocation)(Directory有两种,StaticDirectory(用于多个注册中心)和RegistryDirectory,代表一组可以用的invoker,有notify方法,用于注册中心的回调,修改methodInvokerMap来存储动态变化的多个invoker,或者router路由的变化,回调在com.alibaba.dubbo.registry.integration.RegistryDirectory.notify()中执行)->com.alibaba.dubbo.registry.integration.RegistryDirectory.doList(Invocation)中执行路由规则.通过directory,在methodInvokerMap中找出所有的invoker.然后在com.alibaba.dubbo.rpc.cluster.directory.AbstractDirectory.list(Invocation)中继续通过router,分为Script和Condition两种,Condition路由就是管理后台配置的路由规则,比如对于某个service,当消费端的ip满足什么条件调用满足对应条件的服务端.可以启用或者禁用某条路由规则。.调用com.alibaba.dubbo.rpc.cluster.router.MockInvokersSelector.route()->com.alibaba.dubbo.rpc.cluster.router.MockInvokersSelector.getNormalInvokers()找出可以执行的invoker。
3.在FailoverClusterInvoker.doInvoke()中,如果是第二次dubbo调用读取到所有符合条件的服务提供者invoker之后,或者第一次dubbo调用进入com.alibaba.dubbo.rpc.cluster.support.AbstractClusterInvoker.select().如果invokers多于一个,进入com.alibaba.dubbo.rpc.cluster.LoadBalance.select().由LoadBalance组件执行负载均衡,从中挑选一个invoker进行调用,默认是进入com.alibaba.dubbo.rpc.cluster.loadbalance.RandomLoadBalance.doSelect()随机算法.框架内置支持的负载均衡算法包括random(随机)、roundrobin(R-R循环)、leastactive(最不活跃)、consistenthash(一致性hash),应用可配置.我调试时只有一台机器提供了一个invoker,所以没有进入这段代码.
4.在FailoverClusterInvoker.doInvoke()中选择好invoker之后,进入com.alibaba.dubbo.rpc.Invoker.invoke(Invocation)->ConsumerContextFilter.invoke()->FutureFilter.invoke()->MonitorFilter.invoke()->com.alibaba.dubbo.rpc.protocol.dubbo.DubboInvoker.doInvoke(Invocation).methodInvokerMap保存的是持有DubboInvoker(dubbo协议)实例的InvokerDelegete对象,是Invoker-Filter链的头部,先激活Filter连然后最终调到DubboInvoker.doInvoke(RpcInvocation),
5.在DubboInvoker.doInvoke()中先获取当前调用的ExchangeClient,如果有多个Client,随机选一个.然后判断调用是不是异步的,单向的,超时时间(默认前两个都不是,超时1秒).远程调用分三种类型: 
a.单向调用,无需获取关注调用结果的,无需等待接口返回结果,注意调用结果不要单纯跟返回值混淆了,异常也是调用结果。 
b.异步调用,需要关注返回结果,但是不会同步等待接口调用结束,会异步的获取返回返回结果,这种情况给调用者返回一个Future,但是不同步等待Future.get返回调用结果 
c.同步调用,需要同步等待服务调用结束获取调用结果,给调用者返回一个Future并且Future.get等待结果,此时接口调用线程会挂起等待响应。
如果使用者配置了多个connections按顺序选择一个ExchangeClient和服务器通信,同步调用时依次调用com.alibaba.dubbo.rpc.protocol.dubbo.ReferenceCountExchangeClient.request()->com.alibaba.dubbo.remoting.exchange.support.header.HeaderExchangeClient.request()(加入client的目的我理解是区分不同的netty,p2p,netty4等网络客户端调用,真正的netty等调用作为channel处理)->这里的request参数是RpcInvocation对象,包含调用的方法、参数等信息,timeout参数是接口超时时间,把这些信息封装在Request对象中,依次调用NettyClient.send()->com.alibaba.dubbo.remoting.transport.netty.NettyChannel.send(),这个channel对象就是和服务端打交道的NettyClient实例,NettyClient.send调用com.alibaba.dubbo.remoting.transport.netty.NettyChannel.send()->org.jboss.netty.channel.Channel.write()->com.alibaba.dubbo.remoting.transport.netty.NettyHandler.writeRequested()->org.jboss.netty.handler.codec.oneone.OneToOneEncoder.handleDownstream()发送消息,在该方法中,先开始进行编码。编码依次进入com.alibaba.dubbo.remoting.transport.netty.NettyCodecAdapter.InternalEncoder.encode()->com.alibaba.dubbo.rpc.protocol.dubbo.DubboCountCodec.encode()->com.alibaba.dubbo.remoting.exchange.codec.ExchangeCodec.encodeRequest(),在该方法中具体进行dubbo协议的编码.在OneToOneEncoder.handleDownstream()中编码结束后,调用com.alibaba.dubbo.remoting.ChannelHandler.sent()发送消息
(四.2)消费端请求编码
1进入ExchangeCodec.encodeRequest().根据协议,消息中写入16个字节的消息头: 
a、1-2字节,固定的魔数 
b、第3个字节,第8位存储数据类型是请求数据还是响应数据,其它7位存储序列化类型,约定和服务端的序列化-反序列化协议 
c、5-12个字节,请求id 
d、13-16个字节,请求数据长度.框架中默认的序列化协议是hessian2。消息体数据包含dubbo版本号、接口名称、接口版本、方法名称、参数类型列表、参数、附加信息,把它们按顺序依次序列化,数据写入到类型为ChannelBuffer的buffer参数中,然后把ChannelBuffer封装成Netty框架的org.jboss.netty.buffer.ChannelBuffer。如果参数中有回调接口,还需要在消费端启动端口监听提供端的回调.数据部分的封装通过com.alibaba.dubbo.rpc.protocol.dubbo.DubboCodec.encodeRequestData()实现。然后把封装好的ChannelBuffer写到链路发送到服务端,这里消费端前半部分的工作就完成.最终通过org.jboss.netty.handler.codec.oneone.OneToOneEncoder.doEncode中调用org.jboss.netty.channel.Channels.write()发送。
(四.3)提供端请求解码
1.当有客户端请求进来时,进入com.alibaba.dubbo.remoting.transport.netty.NettyCodecAdapter.InternalDecoder.messageReceived(),调用com.alibaba.dubbo.rpc.protocol.dubbo.DubboCountCodec.decode()->com.alibaba.dubbo.rpc.protocol.dubbo.DubboCodec.decode()->com.alibaba.dubbo.rpc.protocol.dubbo.DubboCodec.decodeBody()按dubbo协议解析dubbo协议头,魔数等.然后调用com.alibaba.dubbo.rpc.protocol.dubbo.DecodeableRpcInvocation.decode()解析出具体的接口类,调用com.alibaba.dubbo.rpc.protocol.dubbo.CallbackServiceCodec.decodeInvocationArgument()解析出参数.
2.在DubboCountCodec.decode()中调用DubboCodec.decode()解析出Request对象后,封装成MultiMessage对象.
3.在InternalDecoder.messageReceived()解析出消息后,调用org.jboss.netty.channel.Channels.fireMessageReceived(ChannelHandlerContext, Object, SocketAddress)通知消息的接受,激活下一个处理器的messageReceived事件.
4.事件进入到下一个处理器NettyHandler,进入com.alibaba.dubbo.remoting.transport.netty.NettyHandler.messageReceived()->com.alibaba.dubbo.remoting.transport.MultiMessageHandler.received()->com.alibaba.dubbo.remoting.exchange.support.header.HeartbeatHandler.received()->com.alibaba.dubbo.remoting.transport.dispatcher.all.AllChannelHandler.received()。通过线程池启动新的线程处理数据.
5.新的处理请求线程进入com.alibaba.dubbo.remoting.transport.dispatcher.ChannelEventRunnable.run()->com.alibaba.dubbo.remoting.transport.DecodeHandler.received()->com.alibaba.dubbo.remoting.exchange.support.header.HeaderExchangeHandler.received()->com.alibaba.dubbo.remoting.exchange.support.header.HeaderExchangeHandler.handleRequest()(创建Response对象),Response对象的body部分调用com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol.requestHandler.new ExchangeHandlerAdapter() {...}.reply(ExchangeChannel, Object)。该方法先获取Invoker对象,然后调用Invoker的invoke方法.
(四.4)提供端处理请求.
1.DubboProtocol中reply方法处理逻辑。调用com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol.getInvoker()查找提供端请求对应的Invoker,在接口提供者初始化时,每个接口都会创建一个Invoker和Exporter,Exporter持有invoker实例,Exporter对象保存在DubboProtocol的exporterMap中。这个map是重点,map的key是由URL生成的serviceKey,格式为com.alibaba.dubbo.demo.DemoService:20880.value是对应的exporter,格式为{com.alibaba.dubbo.demo.DemoService:20880=com.alibaba.dubbo.registry.integration.RegistryProtocol$InvokerDelegete@59babb62}.exporter的属性中有对应的Invoker.此时通过Invocation中的信息就可还原该serviceKey并且找到对应的Exporter和Invoker,在分析提供者初始化代码时知道它是Invoker-Filter的头节点,激活Filter后调用由ProxyFactory生成的Invoker.进入com.alibaba.dubbo.rpc.proxy.javassist.JavassistProxyFactory.getInvoker(...).new AbstractProxyInvoker() {...}.doInvoke()调用处理具体的处理方法。
2.在DubboProtocol中的reploy方法中,调用com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper.buildInvokerChain(...).new Invoker() {...}.invoke(Invocation)这是一个链式的filter.每一个filter好像是一个protocol,具体的filter有EchoFilter,ClassLoaderFilter,ContextFilter,TraceFilter,TimeoutFilter,MonitorFilter,ExceptionFilter,DelegateProviderMetaDataInvoker->invoke->com.alibaba.dubbo.rpc.proxy.javassist.JavassistProxyFactory.getInvoker(...).new AbstractProxyInvoker() {...}.doInvoke()->com.alibaba.dubbo.demo.provider.DemoServiceImpl.sayHello(String)真正的实现类.
(四.5)提供端响应结果编码
在HeaderExchangeHandler.received()方法中调用HeaderExchangeHandler.handleRequest()执行完具体的interface实现类后,返回Response对象。调用com.alibaba.dubbo.remoting.transport.netty.NettyChannel.send(Object, boolean)。类似步骤(四.1)中的5进行编码发送处理.通过Code2组件和请求编码时使用的组件一样,把响应类型和响应结果依次写回到客户端,根据协议会写入16个字节的数据头,包括: 
a、1-2字节魔数 
b、第3个字节,序列化组件类型,约定和客户端的序列化-反序列化协议 
c、第4个字节,响应状态,是OK还是error 
d、5-13个字节,响应id,这里的id和request中的id一样 
e、13-16个字节,响应数据长度
此步调用com.alibaba.dubbo.remoting.exchange.codec.ExchangeCodec.encodeResponse执行。返回结果有三种结果:1、没有返回值即返回类型是void;2、有返回值并且执行成功;3、服务调用异常。此步调用com.alibaba.dubbo.rpc.protocol.dubbo.DubboCodec.encodeResponseData执行。解码后的数据会写入到通信链路中.
(四.6)消费端响应结果解码
1.服务端给客户端回写数据之后,客户端会收到IO事件,一个上行事件。NettyClient中有两个上行事件处理器NettyCodecAdapter.decoder和NettyHandler,按照顺序decoder先执行对服务端传过来的数据进行解码,解析出序列化协议、响应状态、响应id(即请求id)。把响应body数据读到DecodeableRpcResult对象中,进行解析同时加载处理原始Request数据,这个Request对象在请求时会被缓存到DefaultFuture中,加载Request的目的是因为Request中Invocation中携带了服务接口的返回值类型信息,需要根据这个类型把响应解析创建对应类型的对象。此步在
com.alibaba.dubbo.rpc.protocol.dubbo.DecodeableRpcResult.decode中实现。
2.创建Response对象并且把解析出结果或异常设置到Response中。 
decoder把响应解析成Response对象中,NettyHandler接着往下处理,同样触发它的messageReceive事件,在提供端解码的时候看到了,它的handler封装关系是:DecodeHandler->HeaderExchangeHandler->DubboProtocol.requestHandler,主要处理在HeaderExchangeHandler中:com.alibaba.dubbo.remoting.exchange.support.header.HeaderExchangeHandler.handleResponse实现。
3.进入com.alibaba.dubbo.remoting.exchange.support.DefaultFuture.doReceived.唤醒调用者线程,并且把Response设置到DefaultFuture中,在消费者触发请求的代码中可以看到,消费端调用接口的时候请求写到提供端之后,会调用DefaultFuture.get阻塞等待响应结果.在done这个Condition上进行条件等待,DefaultFuture.doReceive时,设置response唤醒done,此时调用线程被唤醒并且检查是否已经有了response(避免假唤醒),唤醒之后返回response中的result,调用端即拿到了接口的调用结果(返回值或异常),整个远程服务接口的调用流程就完成了.
(四.6)超时处理
在进行接口调用时会出现两种情况:接口调用成功、接口调用异常,其实还有一种情况就是接口调用超时。在消费端等待接口返回时,有个timeout参数,这个时间是使用者设置的,可在消费端设置也可以在提供端设置,done.await等待时,会出现两种情况跳出while循环,一是线程被唤醒并且已经有了response,二是等待时间已经超过timeout,此时也会跳出while,当跳出while循环并且Future中没有response时,就说明接口已超时抛出TimeoutException,框架把TimeoutException封装成RpcException抛给应用层

参考链接:
生产者提供服务过程
http://www.cnblogs.com/man-li/p/4326580.html 
http://www.cnblogs.com/man-li/p/4323281.html
 http://www.cnblogs.com/man-li/p/4323277.html
http://blog.csdn.net/jycwl/article/details/51243416
http://blog.csdn.net/pentiumchen/article/details/53227640
http://blog.csdn.net/pentiumchen/article/details/53227718
http://blog.csdn.net/pentiumchen/article/details/53227844
消费端提供过程
http://blog.csdn.net/pentiumchen/article/details/53227718
网络调用封装过程
http://blog.csdn.net/pentiumchen/article/details/53227844

猜你喜欢

转载自blog.csdn.net/feivirus/article/details/80211121