dubbo源码系列9-集群容错之dubbo集群 Cluster

一、前沿

现在的应用为了解决单点故障的问题,通常都会将应用部署到至少两台机器上,对于负载较高的服务,还会部署更多的机器支撑业务。同理在 dubbo 中同样会有多个 provider 为同一服务提供服务,此时 consumer 就需要决定选择调用哪一个 provider。此外服务调用失败时的处理逻辑也需要考虑设计,比如:重试、抛出异常或者是只输出异常等,为了解决这些问题,dubbo 定义了集群接口 Cluster 以及 Cluster InvokerCluster 用途是将多个 provider 合并为一个 Cluster Invoker,并将这个 Invoker 暴露给 consumer,这样的话 consumer 只需要通过这个 Invoker 进行远程调用即可。至于具体该调用哪个 provider 以及调用失败后该如何处理等问题,全部交给集群模块处理。 集群模块是 provider 和 consumer 的中间层,为 consumer 屏蔽了 provider 的情况,consumer 只需要调用服务即可,而不需要关心 provider 的具体情况

本文涉及到的负载均衡选择 Invoker 这里没有详细讲解,有专门的文章分析了,如果想了解的话,请查看 负载均衡 文章


Dubbo 提供了多种集群实现,包含但不限于 FailoverCluster 、FailfastCluster、FailsafeCluster、FailbackCluster、ForkingCluster 等,如下图所示:

每种类型的集群实现用途不同,接下来会一一进行分析

二、集群容错

在分析dubbo集群源码前,我们有必要先了解一下集群容错的所有组件,其中包含:Cluster、Cluster Invoker、Directory、Router 和 LoadBalance 等,关系图如下:

dubbo 集群工作分为如下两个阶段:

1、第一个阶段是在 consumer 初始化期间,集群 Cluster 实现类为 consumer 创建 Cluster Invoker 实例,即上图中的 merge 操作

2、第二个阶段是在 consumer 进行远程调用时,以 FailoverClusterInvoker(默认) 为例,经历了如下过程:

1)、该类型 Cluster Invoker 首先会调用 Directory 的 list 方法列举 Invoker 列表(可将 Invoker 简单理解为 provider),调用  Router 的 route 方法进行路由,过滤掉不符合路由规则的 Invoker

2)、FailoverClusterInvoker 拿到 Directory 返回的 Invoker 列表后,它会通过 LoadBalance 从 Invoker 列表中选择一个 Invoker

3)、FailoverClusterInvoker 将参数传给 LoadBalance 选择出的 Invoker 实例的 invoker 方法,进行真正的远程调用

Directory: 用途是保存 Invoker,可简单类比为 List<Invoker>。其实现类 RegistryDirectory 是一个动态服务目录,可感知注册中心配置的变化,它所持有的 Invoker 列表会随着注册中心内容的变化而变化。每次变化后,RegistryDirectory 会动态增删 Invoker,并调用 Router 的 route 方法进行路由,过滤掉不符合路由规则的 Invoker

以上就是集群工作的整个流程,这里并没介绍集群是如何容错的。Dubbo 主要提供了以下常用的六种容错方式:

Failover Cluster:失败自动切换

Failfast Cluster:快速失败

Failsafe Cluster:失败安全

Failback Cluster:失败自动恢复

Forking Cluster:并行调用多个服务提供者

Broadcast Cluster:逐个调用服务提供者,一个异常就抛出错误

三、集群源码

3.1 Cluster 实现类

集群接口 Cluster  是一个接口,只有一个 join 方法,如下图:

Cluster 接口仅用于创建生成 Cluster Invoker。下面我们以 FailoverCluster(默认) 为例,来看一下源码:

public class FailoverCluster implements Cluster {

    public final static String NAME = "failover";

    @Override
    public <T> Invoker<T> join(Directory<T> directory) throws RpcException {
        // 创建并返回 FailoverClusterInvoker 对象
        return new FailoverClusterInvoker<T>(directory);
    }

}

所有类型的 Cluster 功能就一个,即创建生成 Cluster Invoker,逻辑相当简单,这里不做额外分析了。下面我们来看一下 Cluster Invoker 的源码

3.2 Cluster Invoker

Cluster Invoker 是一种 Invoker,provider 的选择逻辑,以及远程调用失败后的的处理逻辑均是封装在 Cluster Invoker 中。

前言中我们知道,集群工作过程可分为两个阶段,第一个阶段是在 consumer 初始化期间,这个在 服务引用的 Invoker 创建中 分析过,这里就不在赘述了。第二个阶段是在 consumer 进行远程调用时,此时 AbstractClusterInvoker(Cluster Invoker 的父类) 的 invoke 方法会被调用,列举 Invoker,负载均衡等操作均会在此阶段被执行。因此下面先来看一下 AbstractClusterInvoker 的 invoke 方法的逻辑,代码如下:

    // AbstractClusterInvoker 的 invoke 方法
    @Override
    public Result invoke(final Invocation invocation) throws RpcException {
        // 校验 Invoker 是否销毁了
        checkWhetherDestroyed();

        // binding attachments into invocation.
        // 将 RpcContext 中的 attachments 参数绑定到 RpcInvocation 中
        Map<String, String> contextAttachments = RpcContext.getContext().getAttachments();
        if (contextAttachments != null && contextAttachments.size() != 0) {
            ((RpcInvocation) invocation).addAttachments(contextAttachments);
        }

        // 从directory中获取 Invoker 列表
        List<Invoker<T>> invokers = list(invocation);
        // 初始化负载均衡策略,RandomLoadBalance 为默认的负载均衡策略
        // invokers 不为空时,则取第一个 invoker 的 url 中 loadbalance 参数设置的负载均衡策略
        LoadBalance loadbalance = initLoadBalance(invokers, invocation);
        // 如果请求是异步的,需要设置 id 参数到 RpcInvocation 的 Attachment 中
        RpcUtils.attachInvocationIdIfAsync(getUrl(), invocation);
        // 调用具体的 Invoker 实现类,dubbo 中默认是 FailoverClusterInvoker,故这里调用 FailoverClusterInvoker 的 doInvoke 方法
        return doInvoke(invocation, invokers, loadbalance);
    }

AbstractClusterInvoker 的 invoke 方法主要做了以下工作:

1)、获取 Invoker 列表,以及初始化加载 LoadBalance

2)、调用模板方法 doInvoke ,即具体的 Invoker 实现类的实现方法

下面我们来看一下 Invoker 列举方法 list(Invocation) 的逻辑,debug 栈流程如下:

debug具体栈信息:

doList:581, RegistryDirectory (org.apache.dubbo.registry.integration)
list:85, AbstractDirectory (org.apache.dubbo.rpc.cluster.directory)
list:290, AbstractClusterInvoker (org.apache.dubbo.rpc.cluster.support)
invoke:249, AbstractClusterInvoker (org.apache.dubbo.rpc.cluster.support)

整个过程源码如下:

    // 1、AbstractClusterInvoker 的 list方法
    protected List<Invoker<T>> list(Invocation invocation) throws RpcException {
        // 调用 AbstractDirectory 的 list 方法获取 Invoker 列表
        return directory.list(invocation);
    }


    // 2、AbstractDirectory 的 list 方法
    @Override
    public List<Invoker<T>> list(Invocation invocation) throws RpcException {
        if (destroyed) {
            throw new RpcException("Directory already destroyed .url: " + getUrl());
        }

        // 调用 RegistryDirectory 的 doList 方法
        return doList(invocation);
    }


    // 3、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).");
        }

        if (multiGroup) {
            return this.invok
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值