天天看点

dubbo源码分析系列(3)服务的引用1 系列目录2 服务引用案例介绍3 服务引用过程4 结束语

<a href="http://my.oschina.net/pingpangkuangmo/blog/508963">dubbo源码分析系列(1)扩展机制的实现</a>

<a href="http://my.oschina.net/pingpangkuangmo/blog/511766">dubbo源码分析系列(2)服务的发布</a>

<a href="http://my.oschina.net/pingpangkuangmo/blog/515673">dubbo源码分析系列(3)服务的引用</a>

<a href="http://my.oschina.net/pingpangkuangmo/blog/521945">dubbo源码分析系列(4)dubbo通信设计</a>

先看一个简单的客户端引用服务的例子,dubbo配置如下:

使用zookeeper作为注册中心

引用远程的helloservice接口服务

helloservice接口内容如下:

利用spring的xml配置创建出一系列的配置对象,存至spring容器中

application对应applicationconfig

registry对应registryconfig

monitor对应monitorconfig

provider对应providerconfig

consumer对应consumerconfig

protocol对应protocolconfig

service对应serviceconfig

reference对应referenceconfig

上面的对象不依赖spring,也就是说你可以手动去创建上述对象。

为了在spring启动的时候,也相应的启动provider发布服务注册服务的过程:又加入了一个和spring相关联的servicebean,继承了serviceconfig

为了在spring启动的时候,也相应的启动consumer发现服务的过程:又加入了一个和spring相关联的referencebean,继承了referenceconfig

利用spring就做了上述过程,得到相应的配置数据,然后启动相应的服务。如果想剥离spring,我们就可以手动来创建上述配置对象,通过serviceconfig和referenceconfig的api来启动相应的服务

具体针对上述案例,则是 根据dubbo:reference配置创建了一个referencebean,该bean又实现了spring的org.springframework.beans.factory.factorybean接口,所以我们如下方式使用时:

使用的不是referencebean对象,而是referencebean的getobject()方法返回的对象。该对象通过代理实现了helloservice接口。所以要看服务引用的整个过程就需要从referencebean的getobject()方法开始入手。

下面来具体说明这个过程。

第一步:收集配置的参数,参数如下:

第二步:从注册中心引用服务,创建出invoker对象

如果是单个注册中心,代码如下:

上述url内容如下:

前面的信息是注册中心的配置信息,如使用zookeeper来作为注册中心

后面refer的内容是要引用的服务信息,如引用helloservice服务

使用协议protocol根据上述的url和服务接口来引用服务,创建出一个invoker对象

第三步:使用proxyfactory创建出一个接口的代理对象,该代理对象的方法的执行都交给上述invoker来执行,代码如下:

下面就来详细的说明下上述第二步和第三步的过程中涉及到的几个概念

protocol、invoker、proxyfactory

分别介绍下invoker、protocol、proxyfactory的概念

invoker一个可执行对象。

这个可执行对象的执行过程分成三种类型:

类型1:本地执行类的invoker

类型2:远程通信执行类的invoker

类型3:多个类型2的invoker聚合成的集群版的invoker

以helloservice接口方法为例:

本地执行类的invoker: server端,含有对应的helloserviceimpl实现,要执行该接口方法,仅仅只需要通过反射执行helloserviceimpl对应的方法即可

远程通信执行类的invoker: client端,要想执行该接口方法,需要需要进行远程通信,发送要执行的参数信息给server端,server端利用上述本地执行的invoker执行相应的方法,然后将返回的结果发送给client端。这整个过程算是该类invoker的典型的执行过程

集群版的invoker:client端,拥有某个服务的多个invoker,此时client端需要做的就是将这个多个invoker聚合成一个集群版的invoker,client端使用的时候,仅仅通过集群版的invoker来进行操作。集群版的invoker会从众多的远程通信类型的invoker中选择一个来执行(从中加入路由和负载均衡策略),还可以采用一些失败转移策略等

所以来看下invoker的实现情况:

dubbo源码分析系列(3)服务的引用1 系列目录2 服务引用案例介绍3 服务引用过程4 结束语

对于客户端来说,invoker则应该是远程通信执行类的invoker、多个远程通信类型的invoker聚合成的集群版的invoker这两种类型。先来说说非集群版的invoker,即远程通信类型的invoker。来看下dubboinvoker的具体实现

大概内容就是:

将通过远程通信将invocation信息传递给服务器端,服务器端接收到该invocation信息后,找到对应的本地invoker,然后通过反射执行相应的方法,将方法的返回值再通过远程通信将结果传递给客户端。

这里分成3种情况:

执行的方法不需要返回值:直接使用exchangeclient的send方法

执行的方法的结果需要异步返回:使用exchangeclient的request方法,返回一个responsefuture,通过threadlocal方式与当前线程绑定,未等服务器端响应结果就直接返回

执行的方法的结果需要同步返回:使用exchangeclient的request方法,返回一个responsefuture,一直阻塞到服务器端返回响应结果

从上面得知服务引用的第二个过程就是:

针对server端来说,会如下使用protocol

protocol要解决的问题就是:根据url中指定的协议(没有指定的话使用默认的dubbo协议)对外公布这个helloservice服务,当客户端根据协议调用这个服务时,将客户端传递过来的invocation参数交给服务器端的invoker来执行。所以protocol加入了远程通信协议的这一块,根据客户端的请求来获取参数invocation invocation。

而针对客户端,则需要根据服务器开放的协议(服务器端在注册中心注册的url地址中含有该信息)来创建相应的协议的invoker对象,如

dubboinvoker

injvminvoker

thriftinvoker

等等

如服务器端在注册中心中注册的url地址为:

会看到上述服务是以dubbo协议注册的,所以这里产生的invoker就是dubboinvoker。我们来具体的看下这个过程

先来看下protocol的接口定义:

我们再来详细看看服务引用的第二步:

protocol的来历是:

refer(interfaceclass, url)的过程即根据url的配置信息来最终选择的protocol实现,默认实现是"dubbo"的扩展实现即dubboprotocol,然后再对dubboprotocol进行依赖注入,进行wrap包装。先来看看protocol的实现情况:

dubbo源码分析系列(3)服务的引用1 系列目录2 服务引用案例介绍3 服务引用过程4 结束语

可以看到在返回dubboprotocol之前,经过了protocolfilterwrapper、protocollistenerwrapper、registryprotocol的包装。

所谓的包装就是如下类似的内容:

使用装饰器模式,类似aop的功能。

所以上述服务引用的过程

中的refprotocol会先经过registryprotocol(先暂时忽略protocolfilterwrapper、protocollistenerwrapper),它干了哪些事呢?

根据注册中心的registryurl获取注册服务registry,将自身的consumer信息注册到注册中心上

//先根据客户端的注册中心配置找到对应注册服务 registry registry = registryfactory.getregistry(url);

//使用注册服务将客户端的信息注册到注册中心上 registry.register(subscribeurl.addparameters(constants.category_key, constants.consumers_category,

上述subscribeurl地址如下:

consumer://192.168.1.104/com.demo.dubbo.service.helloservice?

该url表述了自己是consumer,同时自己的ip地址是192.168.1.104,引用的服务是com.demo.dubbo.service.helloservice,以及注册时间等等

创建一个registrydirectory,从注册中心中订阅自己引用的服务,将订阅到的url在registrydirectory内部转换成invoker

registrydirectory&lt;t&gt; directory = new registrydirectory&lt;t&gt;(type, url); directory.setregistry(registry); directory.setprotocol(protocol); directory.subscribe(subscribeurl.addparameter(constants.category_key,

上述registrydirectory是directory的实现,directory代表多个invoker,可以把它看成list类型的invoker,但与list不同的是,它的值可能是动态变化的,比如注册中心推送变更。

registrydirectory内部含有两者重要属性:

注册中心服务registry registry

protocol protocol。

它会利用注册中心服务registry registry来获取最新的服务器端注册的url地址,然后再利用协议protocol protocol将这些url地址转换成一个具有远程通信功能的invoker对象,如dubboinvoker

然后使用cluster cluster对象将上述多个invoker对象(此时还没有真正创建出来,异步订阅,订阅成功之后,回调时才会创建出invoker)聚合成一个集群版的invoker对象。

cluster cluster = extensionloader.getextensionloader(cluster.class).getadaptiveextension();

cluster.join(directory)

这里再详细看看cluster接口:

只有一个功能就是把上述directory(相当于一个list类型的invoker)聚合成一个invoker,同时也可以对list进行过滤处理(这些过滤操作也是配置在注册中心的)等实现路由的功能,主要是对用户进行透明。看看接口实现情况:

dubbo源码分析系列(3)服务的引用1 系列目录2 服务引用案例介绍3 服务引用过程4 结束语

默认采用的是failovercluster,看下failovercluster:

仅仅是创建了一个failoverclusterinvoker,具体的逻辑留在调用的时候即调用该invoker的invoke(final invocation invocation)方法时来进行处理。其中又会涉及到另一个接口loadbalance(从众多的invoker中挑选出一个invoker来执行此次调用任务),接口如下:

实现情况如下:

dubbo源码分析系列(3)服务的引用1 系列目录2 服务引用案例介绍3 服务引用过程4 结束语

默认采用的是随机策略,具体的内容就请各自详细去研究。

前一篇文章已经讲过了,对于server端,proxyfactory主要负责将服务如helloserviceimpl统一进行包装成一个invoker,这些invoker通过反射来执行具体的helloserviceimpl对象的方法。而对于client端,则是将上述创建的集群版invoker创建出代理对象。

接口定义如下:

proxyfactory的接口实现有jdkproxyfactory、javassistproxyfactory,默认是javassistproxyfactory, jdkproxyfactory内容如下:

可以看到是利用jdk自带的proxy来动态代理目标对象invoker。所以我们调用创建出来的代理对象如helloservice helloservice的方法时,会执行invokerinvocationhandler中的逻辑:

可以看到还是交给目标对象invoker来执行。

本文简略地介绍了客户端引用服务过程以及涉及到的几个概念,接下来的打算是:

客户端与服务器端网络通信模块