上篇我们介绍了Consumer消费端服务列表刷新原理,地址如下
Dubbo源码解读-Consumer消费端服务列表刷新-CSDN博客
本文主要针Dubbo消费端调用服务端流程,从dubbo源码角度进行解析。
大家可以好好仔细读一下本文。有疑问欢迎留言。
接着说明,读Dubbo源码最好是先对Spring源码有一定的了解。如果大家需要,我也可以针对Spring框架做一系列源码的解读专栏。
不过不用担心,如果需要Spring的源码知识,文章中也会进行Spring源码铺垫介绍的。
如果内容中有没描述清楚的,或者大家在阅读源代码有疑问的,欢迎留言,看到就会及时回复。
主要内容
- 类的调用流程
- 核心类
- Mock调用原理
- 消费端调用流程:消费端如何通过依赖注入代理对象,调用到服务端的
- 关于如何依赖注入的原理请查看文章:Dubbo源码解读-Consumer消费端@Reference服务端引用流程-CSDN博客
消费端调服务端流程
消费端依赖注入服务端代理对象后,调用过程整体细节是比较复杂的。因为好多同学为了应付面试或者觉得细节流程太繁琐或者复杂,只想弄清楚相对简练的过程。所以对细节流程做了一个流程概述的部分,方便大家理解大概过程,以及应对面试。更详细的过程可关注详细流程。
类的调用流程
ReferenceBeanInvocationHandler.invoker()->InvokerInvocationHandler.invoker()->MockClusterInvoker.invoke()->FailOverClusterInvoker.invoker()[或者是其他集群容错配置类]->DubboInvoker.doInvoke()->调用NettyClient发送消息和接收服务端响应
其中NettyClient初始化以及调用流程会在后续文章详细介绍,都属于Netty的Api。
本文不需要关注。调用流程只需要了解到DubboInvoker即可。
核心类:
- MockClusterInvoker.invoke:消费端调用的入口逻辑。
- FailOverClusterInvoker.invoke:默认集群容错算法。调用了负载均衡算法,选择invoker。实现集群容错功能
- MockInvoker.invoke:实现Mock原理
- RandomLoadBalance:默认负载均衡器。随即权重,实现负载均衡算法
Mock实现原理
消费端以来注入的代理类,最终被代理的类为MockerClusterInvoker。实现Mock功能以及远程RPC调用功能。
- URL中获取Mock配置
- 如果没有mock信息,则走远程RPC调用
- 如果是force:则强制降级,调用本地方法(Mock原理,下面流程会详细叙述)
- 其他:则走RPC异常降级,先调用远程服务,如果发生RPC异常,则调用本地mock方法(2+3)
流程概述
消费端依赖代理对象,代理调用的入口为MockClusterInvoker.invoke持有集群容错类Cluster,默认FailOverCluster。MockClusterInvoker实现了Mock调用的原理
概述总结:
- MockClusterInvoker.invoke
- 从Url上获取配置的mock信息
- 如果没有mock走远程RPC。
- 获取服务列表(订阅事件,服务列表本地缓存)
- 获取负载均衡算法。抽象类AbstractClusterInvoker
- 调用子类具体集群容错类的doInvoke(),实现远程服务调用。
- 调用负载均衡算法选择一个Invoker.
- FailOverClusterInvoker:根据retry循环重试
- FailBackClusterInvoker:调用失败,则返回空new RpcResult(),并记录失败请求,异步定时调用
- FailFastClusterInvoker:如果RpcExpection,则直接抛出,不重试。
- FailSafeClusterInvoker:如果RpcExpection,记录日志,返回空new RpcResult()
- ForkingClusterInvoker:同时调用多台主机,返回响应最快主机的结果。
- BroadcastClusterInvoker:服务列表挨个调用一边,场景:刷新本地缓存。
- AvailableClusterInvoker.
- 如果force是强制降级,则走本地mock.
- 其他。走RPC异常降级。即先远程,如果发生RPC异常,则走本地(3+4)
更进一步总结
- MockClusterInvoker.invoke
- 从Url上获取配置的mock信息
- 如果没有mock走远程RPC。
- 获取服务列表(订阅事件,服务列表本地缓存)
- 获取负载均衡算法。抽象类AbstractClusterInvoker
- 获取集群容错算法。
- 利用负载均衡算法,选择一个Invoker
- 调用Invoker,判断是否失败。失败则执行集群容错
- 如果force是强制降级,则走本地mock.
- 其他。走RPC异常降级。即先远程,如果发生RPC异常,则走本地(3+4)
详细流程
- MockClusterInVoker.invoker():消费端调用核心流程
- 获取mock配置,是否有配置mock。
- directory.getUrl().getMethodParameter(invocation.getMethodName(), Constants.MOCK_KEY
- 如果没有,直接RPC调用
- this.invoker.invoke(invocation);其中this.invoker指FailoverClusterInvokerr
- AbstractClusterInvoker.invoke()
- 获取服务列表List<Invoker>
- RegistryDiretory.List
- doList()
- 从缓存methodInvokerMap中,根据方法名称,获取对应的List<Invoker>
- 根据routers进行过滤
- 返回服务列表List<Invoker>
- SPI根据负载均衡算法:默认是random加全随机
- FailoverClusterInvoker.doInvoke(invocation, invokers, loadbalance)核心逻辑调用
- 获取重试次数配置
- 根据重试次数循环调用
- 每次重试都重新获取最新的服务列表
- 根据负载均衡算法选取一个服务
- invoker = select(loadbalance, invocation, copyinvokers, invoked);
- 此处的invoked是指RPC失败后,重试过的invoker,用于下次重试时,排除已经试过的节点
- sticky判断是否粘带连接,是则返回之前调用过的invoker
- doSelect()
- 如果invokers只有一个则直接返回。
- 调用具体负载均衡算法,选择一个服务
- Invoker<T> invoker = loadbalance.select(invokers, getUrl(), invocation);
- 如果当前节点是已经试过的节点,则重新选一个invoker.
- 返回当前选中的invoker
- invoker = select(loadbalance, invocation, copyinvokers, invoked);
- 记录已经调用过的invoker
- 调用Invoker: Result result = invoker.invoke(invocation);
- InvokerDelegate->Filter1-》Filter2-〉DubboInvoker
- DubboInvoker.doInvoker
- 单工通信:消费端注解配置。method:isReturn=false;
- 不需要返回值,减少一次服务端给客户端响应的TCP通信
- 不需要创建DefaultFeature
- 异步处理:
- netty本来就是异步通信,不等待返回结果。返回ResponseFuture设置到Rpc上下文。
- 需要返回结果时,调用ResponseFuture.get()阻塞等待即可
- 双端通信
- 等待服务端响应。服务端Netty通讯,有返回值的时候,服务端会回调Netty客户端的Read方法,会唤醒get等待的方法
- return (Result) currentClient.request(inv, timeout).get();
- 单工通信:消费端注解配置。method:isReturn=false;
- 如果是force:则强制降级,调用本地方法(Mock原理)
- doMockInvoke():其实这里会从缓存服务列表methodInvokerMap取,不过取不到mock类型的Invoker.
- 创建MockInvoker对象
- MockInvoker.invoker()
- 获取mock配置,并处理
- return:截取return后内容,包装数据类型直接返回RpcResult
- throw:截取throw后内容,实例化异常类型,直接抛出
- 其他:调用具体的mock实现类中的方法进行降级。
- mock=true:消费端实现类名称为接口名称+Mock。
- 具体实现类。mock="com.ab.c.servcie":消费端实现类名称为mock中配置的借口
- 通过代理工厂javassisProxyFactrory将mock具体类专为Invoker,包装成abstractProxyInvoker.持有Wrapper,包装目标类。返回AbstractProxyInvoker
- 调用:AbstractProxyInvoker.invoker->doinvoker()->wrapper.invokeMethod(proxy, methodName, parameterTypes, arguments);
- RPC或本地Mock方法的调用都会通过Invoker调用wrapper再调用被代理的方法。
- 其他则走RPC异常降级,先调用远程服务,如果发生RPC异常,则调用本地mock方法(2+3)
源码分析
1.入口
MockClusterInvoker.invoker()
public Result invoke(Invocation invocation) throws RpcException {
Result result = null;
String value = directory.getUrl().getMethodParameter(invocation.getMethodName(), Constants.MOCK_KEY, Boolean.FALSE.toString()).trim();
//如果没有mock,直接掉后端服务
if (value.length() == 0 || value.equalsIgnoreCase("false")) {
//no mock
//this.invoker:持有的集群容错类,默认FailOverClusterInvoker
result = this.invoker.invoke(invocation);
//force是强制降级,不掉后端服务
} else if (value.startsWith("force")) {
//force:direct mock
result = doMockInvoke(invocation, null);
} else {
//fail-mock
//非强制降级,而是调用异常降级
try {
result = this.invoker.invoke(invocation);
} catch (RpcException e) {
if (e.isBiz()) {
throw e;
} else {
//降级逻辑
result = doMockInvoke(invocation, e);
}
}
}
return result;
}
2.远程调用
2.1AbstractClusterInvoker.invoke 。本地缓存获取服务列表,选择负载均衡算法。
public Result invoke(final Invocation invocation) throws RpcException {
checkWhetherDestroyed();
LoadBalance loadbalance = null;
//获取服务列表(从本地缓存中获取,本地服务列表来自于,消费端订阅,服务列表舒心事件)
List<Invoker<T>> invokers = list(invocation);
if (invokers != null && !invokers.isEmpty()) {
//获取负载均衡算法
loadbalance = ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(invokers.get(0).getUrl()
.getMethodParameter(RpcUtils.getMethodName(invocation), Constants.LOADBALANCE_KEY, Constants.DEFAULT_LOADBALANCE));
}
RpcUtils.attachInvocationIdIfAsync(getUrl(), invocation);
//调用核心逻辑
return doInvoke(invocation, invokers, loadbalance);
}
2.2FailOverClusterInvoker.doInvoke:调用负载均衡算法,选择一个Invoker。如果调用失败,则根据集群容错算法进行重试。
public Result doInvoke(Invocation invocation, final List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {
List<Invoker<T>> copyinvokers = invokers;
String methodName = RpcUtils.getMethodName(invocation);
//获取重试次数
int len = getUrl().getMethodParameter(methodName, Constants.RETRIES_KEY, Constants.DEFAULT_RETRIES) + 1;
if (len <= 0) {
len = 1;
}
// retry loop.
RpcException le = null; // last exception.
//已经调用过了的服务列表
List<Invoker<T>> invoked = new ArrayList<Invoker<T>>(copyinvokers.size()); // invoked invokers.
Set<String> providers = new HashSet<String>(len);
for (int i = 0; i < len; i++) {
//Reselect before retry to avoid a change of candidate `invokers`.
//NOTE: if `invokers` changed, then `invoked` also lose accuracy.
//如果掉完一次后,服务列表更新了,再次获取服务列表
if (i > 0) {
checkWhetherDestroyed();
copyinvokers = list(invocation);
// check again
checkInvokers(copyinvokers, invocation);
}
//根据负载均衡算法,选择一个服务调用
Invoker<T> invoker = select(loadbalance, invocation, copyinvokers, invoked);
//记录已经调用过的invoker
invoked.add(invoker);
RpcContext.getContext().setInvokers((List) invoked);
try {
//具体的服务调用逻辑
Result result = invoker.invoke(invocation);
return result;
} catch (RpcException e) {
if (e.isBiz()) { // biz exception.
throw e;
}
le = e;
} catch (Throwable e) {
le = new RpcException(e.getMessage(), e);
} finally {
providers.add(invoker.getUrl().getAddress());
}
}
}
2.3 AbstractClusterInvoker.select和doSelect 负载均衡选择invoker
protected Invoker<T> select(LoadBalance loadbalance, Invocation invocation, List<Invoker<T>> invokers, List<Invoker<T>> selected) throws RpcException {
if (invokers == null || invokers.isEmpty())
return null;
String methodName = invocation == null ? "" : invocation.getMethodName();
boolean sticky = invokers.get(0).getUrl().getMethodParameter(methodName, Constants.CLUSTER_STICKY_KEY, Constants.DEFAULT_CLUSTER_STICKY);
//粘带连接
{
//ignore overloaded method
if (stickyInvoker != null && !invokers.contains(stickyInvoker)) {
stickyInvoker = null;
}
//ignore concurrency problem
if (sticky && stickyInvoker != null && (selected == null || !selected.contains(stickyInvoker))) {
if (availablecheck && stickyInvoker.isAvailable()) {
return stickyInvoker;
}
}
}
//负载均衡核心逻辑
Invoker<T> invoker = doSelect(loadbalance, invocation, invokers, selected);
//如果是粘带服务,记录上一次调用的invoke个全局变量
if (sticky) {
stickyInvoker = invoker;
}
return invoker;
}
private Invoker<T> doSelect(LoadBalance loadbalance, Invocation invocation, List<Invoker<T>> invokers, List<Invoker<T>> selected) throws RpcException {
if (invokers == null || invokers.isEmpty())
return null;
if (invokers.size() == 1)
return invokers.get(0);
if (loadbalance == null) {
loadbalance = ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(Constants.DEFAULT_LOADBALANCE);
}
//掉具体的负载均衡算法的select
Invoker<T> invoker = loadbalance.select(invokers, getUrl(), invocation);
//If the `invoker` is in the `selected` or invoker is unavailable && availablecheck is true, reselect.
//如果selected不为空,说明前一次调用失败了,这一个调用是重试调用
//如果当前选择的invoker和前一次失败的invoker是同一个,则再选一次
if ((selected != null && selected.contains(invoker))
|| (!invoker.isAvailable() && getUrl() != null && availablecheck)) {
try {
Invoker<T> rinvoker = reselect(loadbalance, invocation, invokers, selected, availablecheck);
if (rinvoker != null) {
invoker = rinvoker;
} else {
//Check the index of current selected invoker, if it's not the last one, choose the one at index+1.
int index = invokers.indexOf(invoker);
try {
//Avoid collision
invoker = index < invokers.size() - 1 ? invokers.get(index + 1) : invokers.get(0);
}
}
}
}
return invoker;
}
2.4 AbstractLoadBalance.select()
public <T> Invoker<T> select(List<Invoker<T>> invokers, URL url, Invocation invocation) {
if (invokers == null || invokers.isEmpty())
return null;
if (invokers.size() == 1)
return invokers.get(0);
return doSelect(invokers, url, invocation);
}
2.5 默认RandomLoadBalance.doSelect()。默认随机权重算法。
protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {
int length = invokers.size(); // Number of invokers
int totalWeight = 0; // The sum of weights
boolean sameWeight = true; // Every invoker has the same weight?
for (int i = 0; i < length; i++) {
int weight = getWeight(invokers.get(i), invocation);
totalWeight += weight; // Sum
if (sameWeight && i > 0
&& weight != getWeight(invokers.get(i - 1), invocation)) {
sameWeight = false;
}
}
if (totalWeight > 0 && !sameWeight) {
// If (not every invoker has the same weight & at least one invoker's weight>0), select randomly based on totalWeight.
int offset = random.nextInt(totalWeight);
// Return a invoker based on the random value.
for (int i = 0; i < length; i++) {
//权重越大,随机数减去小于0的概率越大
offset -= getWeight(invokers.get(i), invocation);
if (offset < 0) {
return invokers.get(i);
}
}
}
//如果权重相同,则是真随机
// If all invokers have the same weight value or totalWeight=0, return evenly.
return invokers.get(random.nextInt(length));
}
3.mock本地调用
3.1 MockClusterInvoker.doMockInvoke
private Result doMockInvoke(Invocation invocation, RpcException e) {
Result result = null;
Invoker<T> minvoker;
List<Invoker<T>> mockInvokers = selectMockInvoker(invocation);
if (mockInvokers == null || mockInvokers.isEmpty()) {
minvoker = (Invoker<T>) new MockInvoker(directory.getUrl());
} else {
minvoker = mockInvokers.get(0);
}
try {
result = minvoker.invoke(invocation);
} catch (RpcException me) {
if (me.isBiz()) {
result = new RpcResult(me.getCause());
} else {
throw new RpcException(me.getCode(), getMockExceptionMessage(e, me), me.getCause());
}
} catch (Throwable me) {
throw new RpcException(getMockExceptionMessage(e, me), me.getCause());
}
return result;
}
3.2 MockInvoker.invoke
public Result invoke(Invocation invocation) throws RpcException {
String mock = getUrl().getParameter(invocation.getMethodName() + "." + Constants.MOCK_KEY);
mock = normalizeMock(URL.decode(mock));
//force:return jack
if (mock.startsWith(Constants.RETURN_PREFIX)) {
//截取return后面的内容
mock = mock.substring(Constants.RETURN_PREFIX.length()).trim();
try {
//获取方法的返回值类型
Type[] returnTypes = RpcUtils.getReturnTypes(invocation);
//根据配置的mock值和返回值类型进行数据包装
Object value = parseMockValue(mock, returnTypes);
//不掉后端服务直接返回包装值
return new RpcResult(value);
} catch (Exception ew) {
throw new RpcException("mock return invoke error. method :" + invocation.getMethodName()
+ ", mock:" + mock + ", url: " + url, ew);
}
//force:throw com.xx.XXException
} else if (mock.startsWith(Constants.THROW_PREFIX)) {
mock = mock.substring(Constants.THROW_PREFIX.length()).trim();
if (StringUtils.isBlank(mock)) {
throw new RpcException("mocked exception for service degradation.");
} else { // user customized class
//实例化用户自定义的异常类
Throwable t = getThrowable(mock);
//异常往上抛
throw new RpcException(RpcException.BIZ_EXCEPTION, t);
}
} else { //impl mock
try {
//调用具体的mock实现类
Invoker<T> invoker = getInvoker(mock);
//调用mock中的方法进行降级
return invoker.invoke(invocation);
} catch (Throwable t) {
throw new RpcException("Failed to create mock implementation class " + mock, t);
}
}
}
4.其他
先远程调用,如果RPC失败,则本地调用。
总结:上面内容中,每个从业务流程和源码角度进行了详细分析,如果大家有疑问或者对文章排版任何方面有建议都可以留言评论,看到都会及时回复大家。
知识总结,分享不易,全文手敲,欢迎大家关注点赞评论收藏。
当然如果觉得文章写的不错,也可点击大赏按钮,深深的鼓励一下博主。哈哈!!!!