Dubbo的高可用机制

博客转载自:https://mrbird.cc/Spring-Boot-Dubbo-Zookeeper.html

Dubbo的一些自身特性确保了Dubbo的高可用,比如当注册中心宕机后,服务提供者和服务消费者仍能通过本地缓存通讯;注册中心对等集群,任意一台宕掉后,将自动切换到另一台;当有多台服务提供者提供服务时,Dubbo内置了几种负载均衡算法,并且服务提供者是无状态的,任意一台宕掉后,不影响使用;我们还可以通过整合Hystrix来实现服务降级。

注册中心

这里模拟一下当所有服务注册中心宕机后,服务提供者和服务消费者是否能够正常通讯。

在上一节的例子上,启动Zookeeper,然后分别启动server-proivder和server-consumer,启动好后关闭Zookeeper,这时候注册中心是宕机的状态:

注册中心

这里模拟一下当所有服务注册中心宕机后,服务提供者和服务消费者是否能够正常通讯。

在上一节的例子上,启动Zookeeper,然后分别启动server-proivder和server-consumer,启动好后关闭Zookeeper,这时候注册中心是宕机的状态:

QQ截图20190325142636.png

访问http://localhost:8081/hello/mrbird看是否可以成功消费服务:

QQ截图20190325142710.png

可以看到服务提供者和服务消费者通讯是正常的,因为注册中心负责服务地址的注册与查找,相当于目录服务,服务提供者和消费者只在启动时与注册中心交互,注册中心全部宕机,不影响已运行的提供者和消费者,消费者在本地缓存了提供者列表。

Dubbo直连

使用注册中心来维护服务可以降低后期维护和拓展的复杂度,降低耦合。不过Dubbo也提供了绕过注册中心的方法,即服务消费者不通过注册中心,而是直接取访问服务提供者来获取服务,这种方式也称为Dubbo直连。

我们在服务消费者的@Reference注解上直接指定服务提供者的地址,即可实现Dubbo直连:

1
2
@Reference(url = "http://127.0.0.1:8080")
private HelloService helloService;

 

负载均衡

在分布式系统中有多台的服务器作为提供者负责处理各种网络请求,当同时有多个请求同时过来时,需要将其均摊在各台服务器上,避免了某台服务器压力过大而某台服务器则闲置的问题。

 

Dubbo提供了四种实现负载均衡的机制:

1、基于权重随机算法的 RandomLoadBalance

这种方式的思想是为每一台服务器设置一个权值,当有请求到来时就按照大体的权重比例为该请求分配服务器。

å¨è¿éæå¥å¾çæè¿°

如图当orderService的消费者请求userService服务时,因为已经提前设置了权重,每个请求都会按照权重的比例分配到相应的服务器上,若有7个服务陆续发出请求,其中有2个请求将会发送到1号服务器,4个将会发送到2号服务器,1个发送到3号服务器。而即便是有权重比例,请求还是会随机分配给每一个服务器,只是大体上会按照权重的比例而分配。

 

2、基于最少活跃调用数算法的 LeastActiveLoadBalance

这种思想会让每一个服务提供者对应一个Active活跃数,刚开始时每台服务器的Active数均为0,每收到一个请求时,Active就加一,当下一次再有请求过来,就会将该请求分配给Active数较小的服务提供者。而在实现上还会为每台服务器设置一个权重,当两台服务提供者的Active相同时且是最小时,就按照权重的大小为请求分配服务器。当然如果服务器的处理效率越高,Active数就会越少

LeastActiveLoadBalance俗称最小活跃数负载均衡,假如现在有一个由Server1、Server2和Server3三个服务提供者构成的集群,在上一次请求中,Server1的耗时为100ms,Server2的耗时为200ms,Server3的耗时为300ms,那么当一个新的请求到来时,会命中耗时最少的那个服务,即Server1。

那个处理快,就给那个处理

 

 

3、基于 hash 一致性的 ConsistentHashLoadBalance

å¨è¿éæå¥å¾çæè¿°

当orderService发送一个带id参数的方法请求时,会根据这个id分配给对应的服务器;当id=1时会将请求分配给服务提供者1,当id=2时,会将请求分配给服务提供者2,以此类推…

 

4、基于加权轮询算法的 RoundRobinLoadBalance

这种算法的思想首先要说到轮询,比如说有三台服务器,第一个请求将会分配给1号服务器,第二个请求分给2号服务器,第三个请求将会分配给3号服务器,第四个请求又重新分配给了1号服务器,以此轮询。而基于加权的轮询算法则是会根据每台服务器的性能为服务器设置一个权值,加权后,每台服务器能够得到的请求数比例,接近或等于他们的权重比。比如服务器 A、B、C 权重比为 5:2:1。那么在8次请求中,服务器 A 将收到其中的5次请求,服务器 B 会收到其中的2次请求,服务器 C 则收到其中的1次请求。

假如现在有一个由三个服务提供者构成的集群,Server1的权重为100,Server2的权重为200,Server3的权重为300,现在有6个服务消费请求依法发送过来,按照轮询机制,第1个请求命中Server1,第2个请求命中Server2,第3个请求命中Server3,到这里三个服务提供者已经轮询完一次,第4个请求本应该从新开始轮询,命中Server1,但是由于Server1的权重为100(占1/6,即6次只会命中一次),所以第4个请求会被分配到Server2。

机制选择

默认情况下,Dubbo采用RandomLoadBalance负载均衡机制。我们可以在服务提供者和服务消费者上指定使用哪种负载均衡:

在客户端的@Reference注解上指定:

1
2
@Reference(loadbalance = RoundRobinLoadBalance.NAME)
private HelloService helloService;

 

在服务端的@Server注解上指定:

1
@Service(interfaceClass = HelloService.class, loadbalance = RoundRobinLoadBalance.NAME)

 

权重分配

我们可以在Dubbo提供的@Server注解上指定暴露服务的权重:

1
@Service(interfaceClass = HelloService.class, weight = 100)

 

这时候在Dubbo Admin里可以看到这个服务的权重就为100了:

QQ截图20190325154803.png

我们可以可以通过Dubbo Admin来动态调节服务的权重:

QQ截图20190325154908.png

集群容错

Dubbo支持多种容错模式:http://dubbo.apache.org/zh-cn/docs/user/demos/fault-tolerent-strategy.html

服务降级

Dubbo默认支持两种降级策略:

  1. mock=force:return+null 表示消费方对该服务的方法调用都直接返回 null 值,不发起远程调用。用来屏蔽不重要服务不可用时对调用方的影响。

  2. 还可以改为 mock=fail:return+null 表示消费方对该服务的方法调用在失败后,再返回 null 值,不抛异常。用来容忍不重要服务不稳定时对调用方的影响。

我们可以在Dubbo Admin控制台上来处理降级。

为了模拟错误情况,我们改造服务提供者实现的hello方法:

1
2
3
4
5
6
7
8
9
10
@Override
public String hello(String message) {
System.out.println("调用 cc.mrbird.provider.service.HelloServiceImpl#hello");
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "hello," + message;
}

 

方法中让线程阻塞了2秒。

接着改造服务消费者,在@Reference注解上配置超时时间:

1
2
@Reference(timeout = 1000)
private HelloService helloService;

 

在不进行服务降级的情况下,访问http://localhost:8081/hello/mrbird将看抛出异常:

QQ截图20190325172215.png

在Dubbo Admin消费者列表上的屏蔽按钮对应mock=force:return+null策略,即不调用服务,直接返回null,

QQ截图20190325172250.png

点击屏蔽后,再次访问http://localhost:8081/hello/mrbird

QQ截图20190325172756.png

服务提供者的控制台也没有任何调用日志:

QQ截图20190325172833.png

而容错按钮则对应mock=fail:return+null机制。点击容错按钮,再次访问http://localhost:8081/hello/mrbird

QQ截图20190325172756.png

QQ截图20190325182525.png

上述结果证明了在mock=fail:return+null策略下,消费方对该服务的方法调用在失败后,再返回 null 值(之所以会输出多次调用日志,是因为Dubbo的重试机制)。

整合Hystrix

我们可以通过整合Spring-Cloud-Hystrix和Dubbo来拓展服务降级。

在server-provider里引入Hystrix依赖:

1
2
3
4
5
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
<version>2.0.2.RELEASE</version>
</dependency>

 

然后再server-provider的入口类上使用@EnableHystrix注解开启Hystrix功能。

接着改造server-provider的HelloServiceImpl:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Service(interfaceClass = HelloService.classE)
@Component
public class HelloServiceImpl implements HelloService {

@Override
@HystrixCommand(fallbackMethod = "defaultHello")
public String hello(String message) {
System.out.println("调用 cc.mrbird.provider.service.HelloServiceImpl#hello");
String a = null;
a.toString();
return "hello," + message;
}

public String defaultHello(String message) {
return "hello anonymous";
}
}

 

hello方法中,我们制造一个空指针异常,通过@HystrixCommand(fallbackMethod = "defaultHello")指定了降级的方法为defaultHello。值得注意的是,降级方法的方法参数和返回类型必须和原方法保持一致,不熟悉的读者可以参考https://mrbird.cc/Spring-Cloud-Hystrix-Circuit-Breaker.html

改造好后,重启服务提供者和服务消费者,访问http://localhost:8081/hello/mrbird

QQ截图20190325191901.png

可见服务已经成功降级。

源码链接:https://github.com/wuyouzhuguli/SpringAll/tree/master/53.Dubbo-High-Availability

 

posted on 2020-09-01 14:46  luzhouxiaoshuai  阅读(672)  评论(0编辑  收藏  举报

导航