集群调用
为了提供性能与保持高可用,通常一个接口能力会有多个提供者,也就是集群(Cluster)。
下面分析一下,Dubbo中一次invoke(调用)的流程。
首先介绍一下几个关键类,可以结合具体流程在进行消化。
各节点关系(参考Dubbo官网)
Cluster
将Directory
中的多个Invoker
伪装成一个Invoker
,对上层透明,伪装过程包含了容错逻辑,调用失败后,重试另一个
Invoker
是Provider
的一个可调用Service
的抽象,Invoker
封装了Provider
地址及Service
接口信息。
Directory
代表多个Invoker
,可以把它看成List<Invoker>
,但与List
不同的是,它的值可能是动态变化的,比如注册中心推送变更Router
负责从多个Invoker
中按路由规则选出子集,比如读写分离,应用隔离等LoadBalance
负责从多个Invoker
中选出具体的一个用于本次调用,选的过程包含了负载均衡算法,调用失败后,需要重选
invoke流程
使用的模板方法设计模式,可以看到一些主流程都在父类的AbstractClusterInvoker中,一些特殊实现如doXxx(比如doList、doInvoke)根据配置的不同子类,或者根据SPI定制化实现。
AbstractClusterInvoker#invoke
public Result invoke(final Invocation invocation) throws RpcException {
//校验服务可用性,是否已经被摧毁
checkWhetherDestroyed();
//将一些上下文信息绑定到invocation中,可以传递到远程服务器
Map<String, Object> contextAttachments = RpcContext.getContext().getObjectAttachments();
if (contextAttachments != null && contextAttachments.size() != 0) {
((RpcInvocation) invocation).addObjectAttachments(contextAttachments);
}
//将可用的invokers全部列出来(DynamicDirectory、RegistryDirectory),再根据router规则进行过滤选出子集,如读写分离等
List<Invoker<T>> invokers = list(invocation);
//进行负载均衡,选择真正调用的一个提供者,默认使用random
LoadBalance loadbalance = initLoadBalance(invokers, invocation);
//若是异步调用则记录调用id,为了幂等
RpcUtils.attachInvocationIdIfAsync(getUrl(), invocation);
//根据集群容错策略进