服务消费者创建服务代理
// demoService 是代理对象,代理对象是 DemoService 接口的实现类
DemoService demoService = (DemoService) context.getBean("demoService");
一、服务消费者创建服务代理简图
总体流程:(默认配置情况下)
- 首先 ReferenceConfig 类的 init 方法调用
Protocol#refer
方法生成 Invoker 实例(如上图中的红色部分),这是服务消费的关键。- 然后使用 JavassistProxyFactory#getProxy 生成接口(DemoService)的代理对象 ref
- 服务引用第一步,有注册中心的情况下(最常用)会调用
RegistryProtocol#refer(Class
,RegistryProtocol 实际上是其他具体 Protocol(eg. DubboProtocol)的 AOP 类,在type, URL url) refer(...)
中:
- 获取注册中心
- 创建 RegistryDirectory(AOP)
- 首先会获取注册中心 Registry,然后进行服务注册;(AOP)
- 订阅providers、configurators、routers
4.1. 做第一次服务发现,获取到 provider 节点(provider 以 URL 进行表示)后;(AOP)
4.2.之后使用具体的 DubboProtocol 将这些表示 provider 的 URL 转化为 DubboInvoker,并且为每一个 provider 创建 nettyClient,与 nettyServer 进行连接
(具体的 Protocol 做的事
)
4.3、进行 DubboInvoker 的缓存(AOP),其中 RegistryDirectory#Map>> methodInvokerMap 是后续发起调用时获取 Invoker 的真正容器( 重要
)
- 将directory封装成一个ClusterInvoker(MockClusterInvoker)。
public class RegistryProtocol implements Protocol {
@Override
public Invoker refer(Class type, URL url) throws RpcException {
...
// 1. 获取注册中心:创建ZkClient实例,连接zk
Registry registry = registryFactory.getRegistry(url);
...
// 2.
return doRefer(cluster, registry, type, url);
}
private Invoker doRefer(Cluster cluster, Registry registry, Class type, URL url) {
// 1. 创建 RegistryDirectory 实例
RegistryDirectory directory = new RegistryDirectory(type, url);
...
// 2. 向注册中心注册服务
registry.register(registeredConsumerUrl);
// 3. 订阅providers、configurators、routers(订阅时,调用了具体的Protocol,eg. DubboProtocol 的 refer(RegistryDirectory),方法,在该方法中,创建了 nettyClient 端,并建立了长连接)
directory.subscribe(subscribeUrl.addParameter("category","providers,configurators,routers"));
// 4. 将directory封装成一个ClusterInvoker(MockClusterInvoker)
Invoker invoker = cluster.join(directory);
...
return invoker;
}
}
大致看下后续发起调用时,是怎么从 RegistryDirectory 中获取可执行的 Invoker 的。
public class RegistryDirectory extends AbstractDirectory implements NotifyListener {
// 以注释处的例子为例,初始化之后:{"sayHello":[A,B], "sayBye":[B], "*":[router过滤后的provider]}
private volatile Map>> methodInvokerMap;
/************************* 初始化更新 newMethodInvokerMap *************************/
// 订阅providers、configurators、routers时,执行的通知逻辑
@Override
public synchronized void notify(List urls) {
// 服务提供者URL
List invokerUrls = new ArrayList();
...
// 初始化 invokerUrls
for (URL url : urls) {
String category = url.getParameter(Constants.CATEGORY_KEY, Constants.DEFAULT_CATEGORY);
...
if (Constants.PROVIDERS_CATEGORY.equals(category)) {
invokerUrls.add(url);
}
}
...
// 只针对 providers 进行调用
refreshInvoker(invokerUrls);
}
private void refreshInvoker(List invokerUrls) {
...
// Translate url list to Invoker map => 将url转换为InvokerDelegate
Map> newUrlInvokerMap = toInvokers(invokerUrls);
// Change method name to map Invoker Map => 构造{"sayHello":InvokerDelegate}这样的键值对
Map>> newMethodInvokerMap = toMethodInvokers(newUrlInvokerMap);
...
// 初始化实例属性 methodInvokerMap
this.methodInvokerMap = newMethodInvokerMap;
...
}
private Map>> toMethodInvokers(Map> invokersMap) {
Map>> newMethodInvokerMap = new HashMap>>();
...
for (Invoker invoker : invokersMap.values()) {
// 1. 获取 provider 的所有方法 methods=xxx,yyy,zzz
// (同一个接口的不同 provider 的 methods 参数可能不同,例如 DemoService#sayHello() 在 providerA 中有,后续添加了 DemoService#sayBye() 之后,部署到了 providerB,
// 此时 providerA 还没部署,这一时刻,进行的服务发现根据 serviceKey 会发现 providerA 和 providerB,但是二者所拥有的方法却是不同的,那么经过如下逻辑后,
// newMethodInvokerMap={"sayHello":[A,B], "sayBye":[B]})
String parameter = invoker.getUrl().getParameter(Constants.METHODS_KEY);
String[] methods = Constants.COMMA_SPLIT_PATTERN.split(parameter);
for (String method : methods) {
...
newMethodInvokerMap.put(method, methodInvokers);
...
}
}
// newMethodInvokerMap={"sayHello":[A,B], "sayBye":[B], "*":[router过滤后的provider]}
newMethodInvokerMap.put(Constants.ANY_VALUE, newInvokersList);
...
return Collections.unmodifiableMap(newMethodInvokerMap);
}
/************************* 从 newMethodInvokerMap 中选择 Invoker *************************/
@Override
public List> doList(Invocation invocation) {
...
List> invokers = null;
Map>> localMethodInvokerMap = this.methodInvokerMap; // local reference
if (localMethodInvokerMap != null && localMethodInvokerMap.size() > 0) {
String methodName = RpcUtils.getMethodName(invocation);
...
// 1. 根据方法名进行 Invoker 的获取:从 {"sayHello":List} 中根据 methodName("sayHello")获取List
if (invokers == null) {
invokers = localMethodInvokerMap.get(methodName);
}
// 2. 根据 key=* 进行获取 List
if (invokers == null) {
invokers = localMethodInvokerMap.get(Constants.ANY_VALUE);
}
// 3. 遍历获取一个 List
if (invokers == null) {
Iterator>> iterator = localMethodInvokerMap.values().iterator();
if (iterator.hasNext()) {
invokers = iterator.next();
}
}
}
return invokers == null ? new ArrayList>(0) : invokers;
}
...
}
Q
: 为什么要按照方法名进行 provider 的缓存而不是直接按照 interfaceName/group/version 这样的格式?
A
: 同一个接口的不同 provider 的 methods 参数可能不同,例如 DemoService#sayHello() 在 providerA 中有,后续添加了 DemoService#sayBye() 之后,部署到了 providerB,同时将新包(包含sayBye())打包给 consumer,consumer 部署完成后,假设此时 providerA 还没部署,这一时刻,consumer 进行的服务发现根据 serviceKey 会发现 providerA 和 providerB,但是二者所拥有的方法却是不同的,那么经过如下逻辑后,newMethodInvokerMap={"sayHello":[A,B], "sayBye":[B]}
),这样后续如果执行 DemoService#sayBye() ,就会直接获取到 B。
注意
:在 2.7.x 中去掉了 newMethodInvokerMap 属性,不再使用方法名作为 key,直接存储 List,代码虽然简化了,但是也丢失了 A
中描述的好处。
服务引用的第二步,见下文第二小节分析和 第10章 Dubbo 代理层的设计与实现
二、服务消费者创建服务代理源码梯形图
ReferenceConfig.init()
-->createProxy(Map map)
//一 获取Invoker
-->RegistryProtocol.refer(Class type, URL url)
//1 获取注册中心:创建ZkClient实例,连接zk
-->Registry registry = registryFactory.getRegistry(url)
-->AbstractRegistryFactory.getRegistry(URL url)
-->ZookeeperRegistryFactory.createRegistry(URL url)
-->new ZookeeperRegistry(URL url, ZookeeperTransporter zookeeperTransporter)
-->ZkclientZookeeperTransporter.connect(URL url)
-->new ZkclientZookeeperClient(URL url)
-->new ZkClient(url.getBackupAddress())
-->AbstractRegistryFactory.Map REGISTRIES.put("zookeeper://10.211.55.5:2181/com.alibaba.dubbo.registry.RegistryService", 上边的ZookeeperRegistry实例)
-->doRefer(Cluster cluster, Registry registry, Class type, URL url)
-->new RegistryDirectory(type, url)
//2 向注册中心注册服务
-->registry.register(url)
-->ZookeeperRegistry.doRegister(URL url)
-->AbstractZookeeperClient.create(String path, boolean ephemeral)
//3 订阅providers、configurators、routers
-->RegistryDirectory.subscribe(URL url)
-->ZookeeperRegistry.doSubscribe(final URL url, final NotifyListener listener)
//3.1 会获取当前节点下已经存在的子节点(第一次服务发现发生在这里),添加子节点变化监听器
-->List children = zkClient.addChildListener(path, zkListener)
-->AbstractRegistry.notify(URL url, NotifyListener listener, List urls)
-->saveProperties(url)
-->RegistryDirectory.notify(List urls)
//仅仅针对的是providers
-->refreshInvoker(List invokerUrls)
-->toInvokers(List urls)
-->ProtocolFilterWrapper.refer(Class type, URL url)
-->DubboProtocol.refer(Class serviceType, URL url)
//3.1.1 创建ExchangeClient,对第一次服务发现providers路径下的相关url建立长连接
-->getClients(URL url)
-->getSharedClient(URL url)
-->ExchangeClient exchangeClient = initClient(url)
-->Exchangers.connect(url, requestHandler)
-->HeaderExchanger.connect(URL url, ExchangeHandler handler)
-->new DecodeHandler(new HeaderExchangeHandler(handler)))
-->Transporters.connect(URL url, ChannelHandler... handlers)
-->NettyTransporter.connect(URL url, ChannelHandler listener)
-->new NettyClient(url, listener)
-->new MultiMessageHandler(HeartbeatHandler(AllChannelHandler(handler)))
-->getChannelCodec(url)//获取Codec2,这里是DubboCountCodec实例
-->doOpen()//开启netty客户端
-->doConnect()//连接服务端,建立长连接
-->new HeaderExchangeClient(Client client, boolean needHeartbeat)//上述的NettyClient实例,needHeartbeat:true
-->startHeatbeatTimer()//启动心跳计数器
-->ReferenceCountExchangeClient(ExchangeClient client, ConcurrentMap ghostClientMap)/
-->Map referenceClientMap.put("10.10.10.10:20880", 上边的ReferenceCountExchangeClient实例)
//3.2 创建DubboInvoker(nettyClient的持有者,真正发起netty调用的Invoker,做两件事:选择nettyClient + 处理单向/异步/同步调用模板)
-->new DubboInvoker(Class serviceType, URL url, ExchangeClient[] clients, Set> invokers)
-->DubboProtocol.Set> invokers.add(上边的DubboInvoker实例)
-->ProtocolFilterWrapper.buildInvokerChain(final Invoker invoker, String key, String group)
-->new InvokerDelegete(Invoker invoker, URL url, URL providerUrl)
//3.3 将创建出来的Invoker缓存起来
-->newUrlInvokerMap.put("dubbo://10.10.10.10:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-consumer&check=false&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=16001®ister.ip=10.10.10.10&remote.timestamp=1510127991625&side=consumer×tamp=1510128022123", 上边的InvokerDelegate实例)
-->toMethodInvokers(newUrlInvokerMap)
-->Map>> newMethodInvokerMap:{sayHello=[InvokerDelegete实例], *=[InvokerDelegete实例]}
//4 将directory封装成一个ClusterInvoker(MockClusterInvoker)
-->cluster.join(directory)
-->Cluster$Adaptive.join(directory)
-->ExtensionLoader.getExtensionLoader(Cluster.class).getExtension("failover")//MockClusterWrapper包装FailoverCluster
-->MockClusterWrapper.join(Directory directory)
-->FailoverCluster.join(Directory directory)
-->new FailoverClusterInvoker(directory)
-->MockClusterInvoker(Directory directory, Invoker invoker)//invoker:上边的FailoverClusterInvoker实例
//二 获取代理
-->JavassistProxyFactory.getProxy(Invoker invoker, Class[] interfaces)//invoker:上边的MockClusterInvoker实例, interfaces:[interface com.alibaba.dubbo.demo.DemoService, interface com.alibaba.dubbo.rpc.service.EchoService]
-->Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker))
-->Proxy.getProxy(ClassLoader cl, Class... ics)//使用javassist获取一个动态类
-->new InvokerInvocationHandler(invoker)//invoker:上边的MockClusterInvoker实例
消费者发布的时候总体做了两件事:创建 Invoker
和 创建 API 接口的代理对象
。
2.1 创建Invoker
1. 获取注册中心:创建 ZkClient 实例,连接 zk
//1 获取注册中心:创建ZkClient实例,连接zk
-->Registry registry = registryFactory.getRegistry(url)
-->AbstractRegistryFactory.getRegistry(URL url)
-->ZookeeperRegistryFactory.createRegistry(URL url)
-->new ZookeeperRegistry(URL url, ZookeeperTransporter zookeeperTransporter)
-->ZkclientZookeeperTransporter.connect(URL url)
-->new ZkclientZookeeperClient(URL url)
-->new ZkClient(url.getBackupAddress())
-->AbstractRegistryFactory.Map REGISTRIES.put("zookeeper://10.211.55.5:2181/com.alibaba.dubbo.registry.RegistryService", 上边的ZookeeperRegistry实例)
与provider相同,不再赘述。
2. 向注册中心注册 consumer 服务
//2 向注册中心注册服务
-->registry.register(url)
-->ZookeeperRegistry.doRegister(URL url)
-->AbstractZookeeperClient.create(String path, boolean ephemeral)
consumer 完成注册后,会在 zk 上创建节点(url 解码后):
/dubbo
- /com.alibaba.dubbo.demo.DemoService
-- /consumers
--- /consumer://10.10.10.10/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=25267&side=consumer×tamp=1510225913509
3. 使用 RegistryDirectory 订阅 providers、configurators、routers 节点
其中,configurators
节点用于覆盖配置(实现“热配置”),routers
节点用于配置路由信息。这里重点说一下 providers
节点,该节点下存储着 DemoService 的服务提供者列表,当该列表发生变化时(添加 provider 机器或者宕机),会通知 consumer 进行 refreshInvoker
操作。看一下 RegistryDirectory 订阅 providers 的逻辑。
//3.1 会获取当前节点下已经存在的字节点(第一次服务发现发生在这里),添加子节点变化监听器
-->List children = zkClient.addChildListener(path, zkListener)
-->AbstractRegistry.notify(URL url, NotifyListener listener, List urls)
-->saveProperties(url)
-->RegistryDirectory.notify(List urls)
//仅仅针对的是providers
-->refreshInvoker(List invokerUrls)
-->toInvokers(List urls
-->ProtocolFilterWrapper.refer(Class type, URL url)
-->DubboProtocol.refer(Class serviceType, URL url)
//3.1.1 创建ExchangeClient,对第一次服务发现providers路径下的相关url建立长连接
-->getClients(URL url)
-->getSharedClient(URL url)
-->ExchangeClient exchangeClient = initClient(url)
-->Exchangers.connect(url, requestHandler)
-->HeaderExchanger.connect(URL url, ExchangeHandler handler)
-->new DecodeHandler(new HeaderExchangeHandler(handler)))
-->Transporters.connect(URL url, ChannelHandler... handlers)
-->NettyTransporter.connect(URL url, ChannelHandler listener)
-->new NettyClient(url, listener)
-->new MultiMessageHandler(HeartbeatHandler(AllChannelHandler(handler)))
-->getChannelCodec(url)//获取Codec2,这里是DubboCountCodec实例
-->doOpen()//开启netty客户端
-->doConnect()//连接服务端,建立长连接
-->new HeaderExchangeClient(Client client, boolean needHeartbeat)//上述的NettyClient实例,needHeartbeat:true
-->startHeatbeatTimer()//启动心跳计数器
-->ReferenceCountExchangeClient(ExchangeClient client, ConcurrentMap ghostClientMap)/
-->Map referenceClientMap.put("10.10.10.10:20880", 上边的ReferenceCountExchangeClient实例)
//3.2 创建DubboInvoker
-->new DubboInvoker(Class serviceType, URL url, ExchangeClient[] clients, Set> invokers)
-->DubboProtocol.Set> invokers.add(上边的DubboInvoker实例)
-->ProtocolFilterWrapper.buildInvokerChain(final Invoker invoker, String key, String group)
-->new InvokerDelegete(Invoker invoker, URL url, URL providerUrl)
//3.3 将创建出来的Invoker缓存起来
-->newUrlInvokerMap.put("dubbo://10.10.10.10:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-consumer&check=false&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=16001®ister.ip=10.10.10.10&remote.timestamp=1510127991625&side=consumer×tamp=1510128022123", 上边的InvokerDelegate实例)
-->toMethodInvokers(newUrlInvokerMap)
-->Map>> newMethodInvokerMap:{sayHello=[InvokerDelegete实例], *=[InvokerDelegete实例]}
总体流程 :
- 首先添加 provider 子节点监听器,同时进行
第一次服务发现
,找出当前注册在 zk 上的 provider 节点。- 然后创建
ReferenceCountExchangeClient
,为每一个provider
创建一条Netty
长连接(开启了Netty
客户端并连接 provider 的Netty
服务端)- 开启心跳定时器
- 缓存 ReferenceCountExchangeClient
- 将创建出来的 ReferenceCountExchangeClient 封装到 DubboInvoker 实例中
- 根据条件获取相关的 filter,之后使用这些 fiter 对 DubboInvoker 实例进行链式包装,将包装后的 DubboInvoker 封装到 InvokerDelegete 中
- 最后将该 InvokerDelegete 实例按照方法名封装到
Map
中。(该 map 也是 consumer 调用 provider 时,获取 provider 实例的地方,该 map 是 RegistryDirectory 的一个属性,所以我们可以>> newMethodInvokerMap 将 RegistryDirectory 看做是一个 provider 的客户端缓存器
)
Directory.List
:routers 会在两个地方被设置
- 初次创建 Directory 实例时,在 Directory 构造器中进行设置;
- notify 的时候会进行 routers 的重新设置。
关于 Directory 的设计,后续分析
4. 将 directory 封装成一个 ClusterInvoker(MockClusterInvoker)
//4 将directory封装成一个ClusterInvoker(MockClusterInvoker)
-->cluster.join(directory)
-->Cluster$Adaptive.join(directory)
-->ExtensionLoader.getExtensionLoader(Cluster.class).getExtension("failover")//MockClusterWrapper包装FailoverCluster
-->MockClusterWrapper.join(Directory directory)
-->FailoverCluster.join(Directory directory)
-->new FailoverClusterInvoker(directory)
-->MockClusterInvoker(Directory directory, Invoker invoker)//invoker:上边的FailoverClusterInvoker实例
Dubbo 实现了集群容错功能。在 consumer 发布的最后流程中,实现了集群容错。
- 首先根据 Dubbo SPI 机制获取指定类型的
Cluster
实现,这里默认是FailoverCluster
(失败重试机制);- 之后将上边的
RegistryDirectory
封装到FailoverClusterInvoker
实例中;- 最后创建一个
MockClusterInvoker
实例,封装了RegistryDirectory
和FailoverClusterInvoker
。(MockClusterInvoker
用于实现服务降级
功能)
到这里,consumer 创建 Invoker 的源码就结束了。总结一下
:
- 获取注册中心:创建 ZkClient 实例,连接 zk
- 向注册中心注册 consumer 服务
- 使用 RegistryDirectory 订阅 providers、configurators、routers 节点
3.1. 添加 provider 子节点监听器,同时进行第一次服务发现,找出当前注册在 zk 上的 provider 节点
3.1.1. 创建 ReferenceCountExchangeClient,为每一个 provider 创建一条 Netty 长连接(开启了 Netty 客户端并连接 provider 的 Netty 服务端)
3.1.2. 开启心跳定时器
3.2. 根据条件获取相关的 filter,之后使用这些 fiter 对DubboInvoker
实例进行链式包装,将包装后的 DubboInvoker 封装到 InvokerDelegete 中
3.3. 最后将该 InvokerDelegete 实例按照方法名封装到Map
中>> newMethodInvokerMap
- 将 directory 封装成一个
ClusterInvoker
(MockClusterInvoker)4.1 首先根据 Dubbo SPI 机制获取指定类型的 Cluster 实现,这里默认是 FailoverCluster(失败重试机制);
4.2 之后将上边的 RegistryDirectory 封装到 FailoverClusterInvoker 实例中;
4.3 最后创建一个 MockClusterInvoker 实例,封装了 RegistryDirectory 和 FailoverClusterInvoker。(MockClusterInvoker用于实现服务降级功能)
我们可以看到,最终获取到的 Invoker 是 MockClusterInvoker 实例
。
关于 Cluster 的设计,后续分析
2.2 创建API接口的代理对象
//二 获取代理
-->JavassistProxyFactory.getProxy(Invoker invoker, Class[] interfaces)//invoker:上边的MockClusterInvoker实例, interfaces:[interface com.alibaba.dubbo.demo.DemoService, interface com.alibaba.dubbo.rpc.service.EchoService]
-->Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker))
-->Proxy.getProxy(ClassLoader cl, Class... ics)//使用javassist获取一个动态类
-->new InvokerInvocationHandler(invoker)//invoker:上边的MockClusterInvoker实例
获取 API 接口代理的逻辑比较简单,注意这里的 Proxy 是 com.alibaba.dubbo.common.bytecode.Proxy
,而非 JDK 的 Proxy。
这里首先调用 Proxy.getProxy(interfaces)
获取到一个创建代理的工厂类 com.alibaba.dubbo.common.bytecode.Proxy0
,如下:
package com.alibaba.dubbo.common.bytecode;
import com.alibaba.dubbo.common.bytecode.ClassGenerator;
import com.alibaba.dubbo.common.bytecode.Proxy;
import com.alibaba.dubbo.common.bytecode.proxy0;
import java.lang.reflect.InvocationHandler;
public class Proxy0 extends Proxy implements ClassGenerator.DC {
public Object newInstance(InvocationHandler invocationHandler) {
return new proxy0(invocationHandler);
}
}
之后调用了 Proxy0#newInstance
方法,创建了一个 com.alibaba.dubbo.common.bytecode.proxy0
实例,该实例就是最终的 DemoService 的代理对象。
DemoService demoService = (DemoService) context.getBean("demoService");
这里的 demoService 就是上述的 com.alibaba.dubbo.common.bytecode.proxy0
实例。
package com.alibaba.dubbo.common.bytecode;
import com.alibaba.dubbo.common.bytecode.ClassGenerator;
import com.alibaba.dubbo.demo.DemoService;
import com.alibaba.dubbo.rpc.service.EchoService;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class proxy0 implements EchoService, DemoService {
public static Method[] methods;
private InvocationHandler handler;
public String sayHello(String string) {
Object[] arrobject = new Object[]{string};
Object object = this.handler.invoke(this, methods[0], arrobject);
return (String)object;
}
public Object $echo(Object object) {
Object[] arrobject = new Object[]{object};
Object object2 = this.handler.invoke(this, methods[1], arrobject);
return object2;
}
public proxy0() {
}
public proxy0(InvocationHandler invocationHandler) {
this.handler = invocationHandler;
}
}
可以看到,当调用 proxy0#sayHello
时,实际上其内部执行的是 InvocationHandler#invoke
,来看一下 InvocationHandler。
public class InvokerInvocationHandler implements InvocationHandler {
private final Invoker invoker; //上边的MockClusterInvoker实例
public InvokerInvocationHandler(Invoker handler) {
this.invoker = handler;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
...
return invoker.invoke(new RpcInvocation(method, args)).recreate();
}
}
InvocationHandler#invoke 调用的又是 MockClusterInvoker#invoke 方法。到此为止,整个服务引用的源码分析就完成了。