dubbo之集群容错

1.简介

为了避免单点故障,现在的应用通常至少会部署在两台服务器上。对于一些负载比较高的服务,会部署更多的服务器。这样,在同一环境下的服务提供者数量会大于1。对于服务消费者来说,同一环境下出现了多个服务提供者。这时会出现一个问题,服务消费者需要决定选择哪个服务提供者进行调用。另外服务调用失败时的处理措施也是需要考虑的,是重试呢,还是抛出异常,亦或是只打印异常等。为了处理这些问题,Dubbo 定义了集群接口 Cluster 以及 Cluster Invoker。集群 Cluster 用途是将多个服务提供者合并为一个 Cluster Invoker,并将这个 Invoker 暴露给服务消费者。这样一来,服务消费者只需通过这个 Invoker 进行远程调用即可,至于具体调用哪个服务提供者,以及调用失败后如何处理等问题,现在都交给集群模块去处理。集群模块是服务提供者和服务消费者的中间层,为服务消费者屏蔽了服务提供者的情况,这样服务消费者就可以专心处理远程调用相关事宜。比如发请求,接受服务提供者返回的数据等。这就是集群的作用。

2.集群容错

这里有必要先来介绍一下集群容错的所有组件。包含 Cluster、Cluster Invoker、Directory、Router 和 LoadBalance 等。

image

集群工作过程可分为两个阶段,第一个阶段是在服务消费者初始化期间,集群 Cluster 实现类为服务消费者创建 Cluster Invoker 实例,即上图中的 merge 操作。第二个阶段是在服务消费者进行远程调用时。以 FailoverClusterInvoker 为例,该类型 Cluster Invoker 首先会调用 Directory 的 list 方法列举 Invoker 列表(可将 Invoker 简单理解为服务提供者)。Directory 的用途是保存 Invoker,可简单类比为 List。其实现类 RegistryDirectory 是一个动态服务目录,可感知注册中心配置的变化,它所持有的 Invoker 列表会随着注册中心内容的变化而变化。每次变化后,RegistryDirectory 会动态增删 Invoker,并调用 Router 的 route 方法进行路由,过滤掉不符合路由规则的 Invoker。当 FailoverClusterInvoker 拿到 Directory 返回的 Invoker 列表后,它会通过 LoadBalance 从 Invoker 列表中选择一个 Invoker。最后 FailoverClusterInvoker 会将参数传给 LoadBalance 选择出的 Invoker 实例的 invoker 方法,进行真正的远程调用。

以上就是集群工作的整个流程。

简而言之: 容错即“耐故障”或“容许故障”的意思。对于组成系统的元器件发生不可避免的故障时,采取响应的措施,仍能使系统维持正常工作状态。

容错模式:是解决容错问题的一系列解决方案。dubbo框架为服务集群容错提供了一系列好的解决方案,在此称为dubbo服务集群容错模式。

Dubbo 主要提供了这样几种容错方式:

  • Failover Cluster - 失败自动切换(默认)

  • Failfast Cluster - 快速失败

  • Failsafe Cluster - 失败安全

  • Failback Cluster - 失败自动恢复

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

Dubbo 提供了多种集群实现,包含但不限于 Failover Cluster、Failfast Cluster 和 Failsafe Cluster 等。每种集群实现类的用途不同,接下来会一一进行分析。

3.搭建基于Apache dubbo 的集群环境

  1. 创建父工程dubbodemo 并引入依赖:
        
            org.apache.dubbo
            dubbo
            2.7.1
        
        
            org.apache.zookeeper
            zookeeper
            3.4.6
        
        
            com.github.sgroschupf
            zkclient
            0.1
        
        
            org.apache.curator
            curator-framework
            2.8.0
        
        
            org.apache.curator
            curator-recipes
            2.8.0
        
        
            log4j
            log4j
            1.2.17
        

2.创建子工程dubbo-service 依赖于dubbodemo工程(服务提供者)

创建接口:

package com.gf.service;

        public interface DemoService {

            String sayHello(String name);
        }

创建接口的实现类:

package com.gf.service.impl;

        import com.gf.service.DemoService;

        public class DemoServiceImpl implements DemoService {
            public String sayHello(String name) {
                System.err.println("service执行了===========");
                return "hello : "+name;
            }
        }

编写provider.xml配置文件:

 
        

            
            

            
            

            
            

            
            

            
            
        

编写Provider.java启动类:

package com.gf.provider;

        import org.springframework.context.support.ClassPathXmlApplicationContext;

        import java.io.IOException;

        public class Provider {

            public static void main(String[] args) throws IOException {
                ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("provider.xml");
                context.start();
                System.in.read();
            }
        }

3.创建子工程dubbo-web 依赖于dubbodemo工程(消费者)

声明接口:(注意:此接口必须和服务提供者接口同包同名)

        package com.gf.service;

        public interface DemoService {

            String sayHello(String name);
        }

编写consumer.xml配置文件:

        
        

            
            

            
            

            
            
        

编写Consumer.java启动类:

package com.gf.consumer;

        import com.gf.service.DemoService;
        import org.springframework.context.support.ClassPathXmlApplicationContext;

        import java.io.IOException;

        public class Consumer {

            public static void main(String[] args) throws IOException {
                ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:consumer.xml");
                context.start();


                DemoService demoService = context.getBean("demoService", DemoService.class);

                String s = demoService.sayHello("dubbo..");
                System.err.println(s);

                System.in.read();
            }
        }

接着我们复制一份dubbo-service改名为dubbo-service1,并修改端口号即可实现模拟多个服务提供者实例即模拟的集群环境.

1.Failover Cluster

当出现失败时,重试其他服务器,为缺省值。通常用于读操作,但重试会带来更长的延迟。可通过retries="2"来设置重试次数(不含第一次)。

幂等(设置重试次数)【查询】、非幂等(不能设置重试次数)【新增】

代码:


2.Failfast Cluster

只发起一次调用,失败立即报错,并且停止当前消费,通常用于非幂等性的写操作,比如:新增记录。

代码:


3.Failsafe Cluster

出现异常时,直接忽略。当前消费者不会停止,待提供者服务恢复,即可正常调用。通常用于写入审计日志等操作。

代码:


4.Failback Cluster

后台记录失败请求,定时重发(消费者不会停止。一段时间后重新再调用,直到调用成功)。通常用于消息通知操作。

代码:


5.Forking Cluster

并行调用多个服务器,只要一个成功即返回。通常用于实时性要求比较高的读操作,但需要浪费更多的服务资源。

可通过forks="2"来设置最大并行数。


4.配置原则

dubbo推荐在Provider上尽量多配置Consumer端属性:【非常重要】

1、作服务的提供者,比服务使用方更清楚服务性能参数,如调用的超时时间,合理的重试次数,等等

2、在Provider配置后,Consumer不配置则会使用Provider的配置值,即Provider配置可以作为Consumer的缺省值。否则,Consumer会使用Consumer端的全局设置,这对于 Provider不可控的,并且往往是不合理的

3、方法级配置别优于接口级别,即小Scope优先

5.多版本

当一个接口实现,出现不兼容升级时,可以用版本号过渡,版本号不同的服务相互间不引用。

可以按照以下的步骤进行版本迁移:

  1. 在低压力时间段,先升级一半提供者为新版本

  2. 再将所有消费者升级为新版本

  3. 然后将剩下的一半提供者升级为新版本

老版本服务提供者配置:


新版本服务提供者配置:


老版本服务消费者配置:


新版本服务消费者配置:


如果不需要区分版本,可以按照以下的方式配置 :


你可能感兴趣的:(dubbo之集群容错)