五Dubbo服务引用源码分析--3服务引用-上
五Dubbo服务引用源码分析--3服务引用-上
5.2 服务引用
在服务引用的部分,同样首先把握整体架构,有个大致流程和方向的把握。在Dubbo官方使用者手册中,对集群容错的架构设计,可以通过其对服务引用的框架结构进行参考:
https://dubbo.gitbooks.io/dubbo-user-book/content/demos/fault-tolerent-strategy.html
上述过程是服务调用call过程,consumer端invoker.invoke,调用provider端的invoker对象的方法,就是RPC调用主干流程。
各节点关系:
- 这里的
Invoker
是Provider
的一个可调用Service
的抽象,Invoker
封装了Provider
地址及Service
接口信息 Directory
代表多个Invoker
,可以把它看成List<Invoker>
,但与List
不同的是,它的值可能是动态变化的,比如注册中心推送变更Cluster
将Directory
中的多个Invoker
伪装成一个Invoker
,对上层透明,伪装过程包含了容错逻辑,调用失败后,重试另一个Router
负责从多个Invoker
中按路由规则选出子集,比如读写分离,应用隔离等LoadBalance
负责从多个Invoker
中选出具体的一个用于本次调用,选的过程包含了负载均衡算法,调用失败后,需要重选
5.2.1 调用的代码流程
上述过程主要是技术设计角度绘出,不是实际调用。该过程只是调用proxy的方法的容错架构设计。不包含refer的初始化过程(初始化过程对directory、registry分别进行初始化)
源码中具体调用过程如下:
在消费端对服务引用初始化的过程中,创建引用的代理对象proxy,其中的结构如下:
代码流程:
proxy0.xxxMethod()
-->InvokerInvocationHandler.invoke
// RpcInvocation [methodName=sayHello, parameterTypes=[class java.lang.String], arguments=[world], attachments={}]
-->MockClusterInvoker.invoke(Invocation invocation)
-->FailoverClusterInvoker.invoke(final Invocation invocation)
-->RegistryDirectory.list(Invocation invocation) //根据RpcInvocation中的methodName获取Invoker
-->router过滤
-->loadBalancer选取一个Invoker
-->执行filter链
// RpcInvocation [methodName=sayHello, parameterTypes=[class java.lang.String], arguments=[world], attachments={path=com.alibaba.dubbo.demo.DemoService, interface=com.alibaba.dubbo.demo.DemoService, version=2.0.0, timeout=60000, group=dev}]
-->DubboInvoker.invoke(Invocation inv)
5.2.2 源码追踪
本文代码使用DatatablesDemo代码。
用applicationContext启动spring,并通过interface加载服务端实现的bean。看出实例对象为dubbo的代理生成的proxy对象
看出该proxy被传入一个invocationHaldler实例InvokerInvocationHandler(该过程就是java的动态代理)
动态代理实现(InvocationHandlerImpl其内含有被代理的实例对象,这里,就是Invoker实例。通过调用invoke方法,调用引用服务端的方法)
java动态代理的创建过程:
// 3.设置一个来自代理传过来的方法调用请求处理器,处理所有的代理对象上的方法调用
InvocationHandler handler = new InvocationHandlerImpl(car);
/*
4.根据上面提供的信息,创建代理对象 在这个过程中,
a.JDK会通过根据传入的参数信息动态地在内存中创建和.class 文件等同的字节码
b.然后根据相应的字节码转换成对应的class,
c.然后调用newInstance()创建实例
*/
Object o = Proxy.newProxyInstance(classLoader, interfaces, handler);
TAG0 InvokerInvocationHandler.invoke
接下来进入dubbo的consumer调用过程,来看容错架构设计
//该类为动态代理的handler对象
public class InvokerInvocationHandler implements InvocationHandler {
// 该处为MockClusterInvoker对象,就是动态代理invocationhandler中实际代理的对象car
private final Invoker<?> invoker;
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String methodName = method.getName(); //getPermission
Class<?>[] parameterTypes = method.getParameterTypes();//方法的参数类型
if (method.getDeclaringClass() == Object.class) {
return method.invoke(invoker, args);
}
if ("toString".equals(methodName) && parameterTypes.length == 0) {
return invoker.toString();
}
if ("hashCode".equals(methodName) && parameterTypes.length == 0) {
return invoker.hashCode();
}
if ("equals".equals(methodName) && parameterTypes.length == 1) {
return invoker.equals(args[0]);
}
//TAG1 MockClusterInvoker/FailoverClusterInvoker.invoke
//proxy代理对象调用方法,代理到invoker.invoke
return invoker.invoke(new RpcInvocation(method, args)).recreate();//根据方法名、参数,创建远程调用对象
}
此时invoker对象,是MockClusterInvoker--->FailoverClusterInvoker,在failover内部,包含一个directory对象,其是一个invoker的list对象,并且具有NotifyListener功能,在zookeeper上订阅节点发生变化时,会刷新invoker的list对象。
TAG1 MockClusterInvoker/FailoverClusterInvoker.invoke
B1 此时,进入MockClusterInvoker类
public class MockClusterInvoker<T> implements Invoker<T> {
private final Directory<T> directory;
//mockClusterInvoker
private final Invoker<T> invoker;
/**……………………………………………………………………………………invoker.invoke调用逻辑……………………………………………………………………………………………………………… */
//
@Override
public Result invoke(Invocation invocation) throws RpcException {
Result result = null;
String value = directory.getUrl().getMethodParameter(invocation.getMethodName(), Constants.MOCK_KEY, Boolean.FALSE.toString()).trim();
/**………………………………………………………………方法参数中不包含force/fail,正常调用…………………………………………………… */
if (value.length() == 0 || value.equalsIgnoreCase("false")) {
//TAG1.1 FailoverClusterInvoker.invoke
//转发给内部的invoker类,调用
result = this.invoker.invoke(invocation);
}
/**………………………………………………………………方法参数中包含force--不调用,直接服务降级…………………………………………………… */
else if (value.startsWith("force")) {
if (logger.isWarnEnabled()) {
logger.info("force-mock: " + invocation.getMethodName() + " force-mock enabled , url : " + directory.getUrl());
}
//force:direct mock
result = doMockInvoke(invocation, null);
}
/**………………………………………………………………方法参数中包含fail--调用失败,服务降级处理…………………………………………………… */
else {
//fail-mock
try {
result = this.invoker.invoke(invocation);
} catch (RpcException e) {
if (e.isBiz()) {
throw e;
} else {
if (logger.isWarnEnabled()) {
logger.warn("fail-mock: " + invocation.getMethodName() + " fail-mock enabled , url : " + directory.getUrl(), e);
}
result = doMockInvoke(invocation, e);
}
}
}
return result;
}
MockClusterInvoker是针对集群部署的注册中心,提供的可以根据URL中mock参数,支持服务降级的集群管理功能的invoker(此处invoker是cluster.join返回的逻辑整合为一个invoker)。mockClusterInvoker内部持有的invoker类型为FailoverClusterInvoker类。
TAG1.1 FailoverClusterInvoker.invoke
FailoverClusterInvoker采用模板方法,基本实现由抽象类AbstractClusterInvoker实现。
进入AbstractClusterInvoker(此时就进入到集群,cluster)
public abstract class AbstractClusterInvoker<T> implements Invoker<T> {
protected final Directory<T> directory;
// AbstractClusterInvoker.invoke(invocation)
public Result invoke(final Invocation invocation) throws RpcException {
checkWhetherDestroyed();
LoadBalance loadbalance; //负载均衡
// binding attachments into invocation.
Map<String, String> contextAttachments = RpcContext.getContext().getAttachments();
if (contextAttachments != null && contextAttachments.size() != 0) {
((RpcInvocation) invocation).addAttachments(contextAttachments);
}
//TAG1.1.1 list(invocation)
//从directory中获取可以正常执行的invoker
List<Invoker<T>> invokers = list(invocation);
if (invokers != null && invokers.size() > 0) { //2
//如果有可用invoker,根据invoker.url的loadbalance参数,决定负载均衡的自适应类
loadbalance = ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(invokers.get(0).getUrl()
.getMethodParameter(invocation.getMethodName(), Constants.LOADBALANCE_KEY, Constants.DEFAULT_LOADBALANCE));
} else {
//如果没有invoker服务,采用默认的负载均衡类 --random
loadbalance = ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(Constants.DEFAULT_LOADBALANCE);
} //2
RpcUtils.attachInvocationIdIfAsync(getUrl(), invocation);
//TAG1.1.2 doInvoke(invocation, invokers, loadbalance)
return doInvoke(invocation, invokers, loadbalance);
}
AbstractClusterInvoker.invoke(invocation)方法的逻辑如下:
1 从directory中获取方法getPermission所对应的invokers;//TAG1.1.1 list(invocation)
2 默认loadbalance负载均衡使用random;如果返回invokers不为空,使用invoker内url参数上的loadBanance-key所设置的值;
3 然后,传入invokers、loadbalance,从返回的invokers中根据负载均衡策略(random随机)中选择一个invoker执行调用逻辑,//TAG1.1.2 doInvoke(invocation, invokers, loadbalance)。
TAG1.1.1 registryDirectory.list(invocation)
protected List<Invoker<T>> list(Invocation invocation) throws RpcException {
//从directory中获取invoker服务
List<Invoker<T>> invokers = directory.list(invocation);
return invokers;
}
list内,RegistryDirectory.list(invocation)获取可用服务invokers
进入模板设计模式的AbstractDirectory中
public abstract class AbstractDirectory<T> implements Directory<T> {
//zookeeper://127.0.0.1:2181/com.alibaba.dubbo.registry.RegistryService?application=demotest-consumer&dubbo=2.0.2&organization=dubbox&owner=programmer&pid=42298&qos.enable=false&refer=application%3Ddemotest-consumer%26dubbo%3D2.0.2%26interface%3Dcom.alibaba.dubbo.demo.DemoService%26methods%3DgetPermissions%26organization%3Ddubbox%26owner%3Dprogrammer%26pid%3D42298%26qos.enable%3Dfalse%26register.ip%3D192.168.0.101%26side%3Dconsumer%26timestamp%3D1672565435211×tamp=1672565435930
private final URL url; //注册中心
//consumer://192.168.0.101/com.alibaba.dubbo.demo.DemoService?application=demotest-consumer&category=providers,configurators,routers&dubbo=2.0.2&interface=com.alibaba.dubbo.demo.DemoService&methods=getPermissions&organization=dubbox&owner=programmer&pid=42298&qos.enable=false&side=consumer×tamp=1672565435211
private volatile URL consumerUrl;//消费者url
//mockInvokerSelector---router路由对象
private volatile List<Router> routers;
从这里看出,Router在Directory内
分析directory的抽象类内模板方法list
public abstract class AbstractDirectory<T> implements Directory<T> {
public List<Invoker<T>> list(Invocation invocation) throws RpcException {
if (destroyed) {
throw new RpcException("Directory already destroyed .url: " + getUrl());
}
//TAG1.1.1.1 AbstractDirectory.doList(invocation)
//先通过directory的模板方法获取可以正常执行的Invoker
List<Invoker<T>> invokers = doList(invocation);
//router位于directory内
List<Router> localRouters = this.routers; // local reference
/**………………………………………………………………………………如果存在路由router…………………………………………………………………………………………………… */
if (localRouters != null && localRouters.size() > 0) {
for (Router router : localRouters) {
try {
if (router.getUrl() == null || router.getUrl().getParameter(Constants.RUNTIME_KEY, true)) {
//TAG1.1.1.2 router.route(invokers, getConsumerUrl(), invocation)
//通过router路由过滤invoker
invokers = router.route(invokers, getConsumerUrl(), invocation);
}
} catch (Throwable t) {
logger.error("Failed to execute router: " + getUrl() + ", cause: " + t.getMessage(), t);
}
}
}
return invokers;
}
AbstractDirectory.list逻辑,是通过doList的模版方法获取invoker的list,然后通过router.route路由过滤invoker。
TAG1.1.1.1 RegistryDirectory.doList()
doList由RegistryDirectory实现(主要作用就是从RegistryDirectory.methodInvokerMap的map缓存中,查找methodName对应的的所有Invoker)
此时,registryDirectory中属性,经过consumer端初始化后,如下所示:
RegistryDirectory内各属性的意义,见[模块详解章节](#4.3 zk订阅)
RegistryDirectory.doList
@Override
public List<Invoker<T>> doList(Invocation invocation) {
if (forbidden) {
// 1. No service provider 2. Service providers are disabled
throw new RpcException(RpcException.FORBIDDEN_EXCEPTION,
"No provider available from registry " + getUrl().getAddress() + " for service " + getConsumerUrl().getServiceKey() + " on consumer " + NetUtils.getLocalHost()
+ " use dubbo version " + Version.getVersion() + ", please check status of providers(disabled, not registered or in blacklist).");
}
List<Invoker<T>> invokers = null;
Map<String, List<Invoker<T>>> localMethodInvokerMap = this.methodInvokerMap; // methodName:invoker的map缓存
if (localMethodInvokerMap != null && localMethodInvokerMap.size() > 0) {
String methodName = RpcUtils.getMethodName(invocation);
Object[] args = RpcUtils.getArguments(invocation);//参数
if (args != null && args.length > 0 && args[0] != null
&& (args[0] instanceof String || args[0].getClass().isEnum())) {
//根据方法名+第一个参数,从methodInvokerMap获取对应的invoker
invokers = localMethodInvokerMap.get(methodName + "." + args[0]); // The routing can be enumerated according to the first parameter
}
if (invokers == null) {
invokers = localMethodInvokerMap.get(methodName);
}
if (invokers == null) {
invokers = localMethodInvokerMap.get(Constants.ANY_VALUE);
}
if (invokers == null) {
Iterator<List<Invoker<T>>> iterator = localMethodInvokerMap.values().iterator();
if (iterator.hasNext()) {
invokers = iterator.next();
}
}
}
return invokers == null ? new ArrayList<Invoker<T>>(0) : invokers;
}
上面过程是从methodInvokerMap中取出invokers。
methodInvokerMap内是以方法名为key,以List
该invokerDelegate有三个参数,provideUrl存储,注册中心下发的provider的服务地址。可以在refer调用服务时候,根据provideUrl、methodparm等参数,重新构造调用服务地址。
三个参数,providerUrl代表当前服务,注册在注册中心中,服务提供者provider的真实地址。
其中,delegate代理类内传入的invoker是[ListenerInvokerWrapper](#2 ListenerInvokerWrapper)的invoker,其内添加了invoker调用和销毁监听器listener。
然后,返回InvokerDelegate类型的invoker实例
TAG1.1.1.2 (Router)MockInvokerSelector.route()
此时,将invokers返回后,从AbstractDirectory的list继续执行list,到达Router进入路由。
getConsumerUrl()=consumer://192.168.0.101/com.alibaba.dubbo.demo.DemoService?application=demotest-consumer&category=providers,configurators,routers&dubbo=2.0.2&interface=com.alibaba.dubbo.demo.DemoService&methods=getPermissions&organization=dubbox&owner=programmer&pid=42298&qos.enable=false&side=consumer×tamp=1672565435211
此时的router为MockInvokerSelector(也是router)
//route路由方法,根据dubbo中配置的路由规则(如读写分离等),获取对应的可用的invokers服务
进入route方法
public class MockInvokersSelector implements Router {
//参数: url为consumer的url
@Override
public <T> List<Invoker<T>> route(final List<Invoker<T>> invokers,
URL url, final Invocation invocation) throws RpcException {
if (invocation.getAttachments() == null) {
//创建正常的invoker
return getNormalInvokers(invokers);
} else {
String value = invocation.getAttachments().get(Constants.INVOCATION_NEED_MOCK);
if (value == null)
return getNormalInvokers(invokers);
/**…………………………………………………………方法调用中,如果需要mock进行本地伪装……………………………………………………………… */
else if (Boolean.TRUE.toString().equalsIgnoreCase(value)) {
return getMockedInvokers(invokers);
}
}
return invokers;
}
MockInvokerSelector.route,根据参数invocation,是否有mock,选择创建normal还是mocked的invoker。
MockInvokersSelector.getNormalInvokers,创建正常的invokers,
MockInvokersSelector
private <T> List<Invoker<T>> getNormalInvokers(final List<Invoker<T>> invokers) {
//判断invoker.getUrl().getProtocol().equals(Constants.MOCK_PROTOCOL)是否是mock协议头的invoker服务类
if (!hasMockProviders(invokers)) {
return invokers;
} else {
//如果获取invokers中不是mock://协议头的invoker,加入list中
List<Invoker<T>> sInvokers = new ArrayList<Invoker<T>>(invokers.size());
for (Invoker<T> invoker : invokers) {
if (!invoker.getUrl().getProtocol().equals(Constants.MOCK_PROTOCOL)) {
sInvokers.add(invoker);
}
}
return sInvokers;
}
}
判断invokers中是否有mock过的提供者,如果没有,直接返回。
回到AbstarctClusterInvoker类,此时从directory执行list获取可用的服务类,已经完成,然后需要执行对应的调用invoke。
总结:
directory中找出本次集群中的全部Invokers;
router中,将上一步的Invokers挑选出正常执行的invokers。(getNomalInvokers)。