Dubbo 是一个分布式服务框架,一个服务可能会部署多个实例,我们应该如何从多个服务提供者组成的集群挑选一个进行调用呢?这就涉及到一个负载均衡的问题。
Dubbo 内置了四种负载均衡策略,分别如下:
下面我们对这四种负载均衡策略做一个简单的介绍。
随机负载均衡是 Dubbo 默认的负载均衡策略,顾名思义,就是从多个服务提供者中随机选择一个。
需要注意的是,Dubbo 的随机负载均衡并非是完全的随机,它有一个权重的概念,会按照权重来设置随机概率,举个例子,我们现在有两个服务提供者,一个的权重是100,另一个的权重是300,那么前者被分配的概率就为 25%,后者被分配的概率为 75%。
我们可以对服务提供者设置不同的权重,例如对性能较好的机器设置大权重,对差一点的机器设置小一点的权重。
轮询负载均衡,即会轮询每一个服务提供者,依次对其进行调用。
轮询负载均衡也有权重的概念,可以严格按照我们设置的比例进行分配,这个是该算法的优点,不过,该算法的缺点也很明显,可能会存在较慢的机器,那么请求会在这台机器上进行累积,很容易导致整个系统变慢。
最少活跃调用数负载均衡会将请求转发至活跃调用数最少的机器上,如果有两台机器活跃数相同,会采取随机负载均衡的策略。
什么是活跃调用数呢?每个服务维护一个活跃数计数器,该计数器存放机器未处理完的请求。当有请求产生时,会选择活跃数最小的机器去执行。
最少活跃调用数负载均衡可以令慢的机器收到更少的请求。
要了解这种负载均衡策略,我们首先得学习一下一致性哈希算法。不会一致性哈希算法的同学可以看一下我之前写的这篇博客,质量保证过硬:一致性哈希算法详解
一致性哈希可以保证相同参数的请求一定会发送到同一台机器上,即使有机器崩溃,由于一致性哈希算法的特性与虚拟节点的存在,发往该机器的请求会被发送到其它机器上,并不会引发剧烈变动。
我们可以在多个地方(客户端,服务端),以多个级别(方法级,接口级,全局配置)配置负载均衡。它们之间的优先关系如下:
我们创建两个服务提供者,两个服务提供者的实现分别如下,它们提供的返回值是不同的。
package edu.szu.producer.serviceImpl;
import com.alibaba.dubbo.config.annotation.Service;
import edu.szu.api.service.NameService;
import org.springframework.stereotype.Component;
@Component
@Service
public class NameServiceImpl implements NameService {
@Override
public String updateName(String name) {
return "旧机器:" + name;
}
}
package edu.szu.producer_new.serviceImpl;
import com.alibaba.dubbo.config.annotation.Service;
import edu.szu.api.service.NameService;
import org.springframework.stereotype.Component;
@Component
@Service
public class NameServiceImpl implements NameService {
@Override
public String updateName(String name) {
return "新机器:" + name;
}
}
我们发起十次远程调用,其返回值分别如下:
旧机器:HelloDubbo
旧机器:HelloDubbo
新机器:HelloDubbo
旧机器:HelloDubbo
新机器:HelloDubbo
新机器:HelloDubbo
新机器:HelloDubbo
旧机器:HelloDubbo
新机器:HelloDubbo
新机器:HelloDubbo
我们可以发现,在默认情况下,其采取的是随机的负载均衡策略。如果我们想要换一种负载均衡策略,例如就换成轮询负载均衡,应该怎么实现呢?
我们修改一下服务消费者的代码,在 @Reference 注解中添加一个 loadbalance 属性来指定负载均衡策略。
package edu.szu.consumer.serviceImpl;
import com.alibaba.dubbo.config.annotation.Reference;
import edu.szu.api.service.NameService;
import edu.szu.consumer.service.ChangeService;
import org.springframework.stereotype.Component;
@Component
public class ChangeServiceImpl implements ChangeService {
@Reference(loadbalance = "roundrobin")
NameService nameService;
@Override
public String change(String name) {
return nameService.updateName(name);
}
}
然后我们再发起十次远程调用,其返回值分别如下:
旧机器:HelloDubbo
新机器:HelloDubbo
旧机器:HelloDubbo
新机器:HelloDubbo
旧机器:HelloDubbo
新机器:HelloDubbo
旧机器:HelloDubbo
新机器:HelloDubbo
旧机器:HelloDubbo
新机器:HelloDubbo
可见其完全遵循了轮询的规则,也证明了我们的负载均衡策略设置成功。
参考:Dubbo的负载均衡