负载均衡组件Ribbon的实现
SpringCloud Ribbon是基于Netflix Ribbon实现的一套客户端负载均衡的工具。
Ribbon是Netflix发布的开源项目,主要功能是提供客户端的软件负载均衡算法和服务调用。
题外话:springcloud一直想把ribbon替换成自己的组件LoadBalancer,但是无奈ribbon属实优秀,且已大规模部署到生产环境中,一时半会儿替代不了,不过未来的趋势可能会变,这也说不好,目前主流还是ribbon。
-
简单的说就是将用户的请求平摊的分配到多个服务上,从而达到系统的HA(高可用)。 常见的负载均衡有软件Nginx,LVS,硬件F5等。
Nginx是服务器负载均衡,客户端所有请求都会交给nginx,然后由nginx实现转发请求。即LB是由服务端实现的。属于集中式LB。
Ribbon是本地负载均衡,在调用微服务接口的时候,会在注册中心上获取注册信息服务列表之后缓存到JVM本地,从而在本地实现RPC远程服务调用。属于进程内LB。
扩展知识 >>
集中式LB:即在服务的消费方和提供方之间使用独立的LB设施(可以是硬件,如F5,也可以是软件,如nginx),由该设施负责把访问请求通过某种策略转发至服务提供方。
在2018年学习springcloud,使用Ribbon需添加相关依赖,现在不需要了,因为Eureka包里就提供了Ribbon的依赖包。
<mapper namespace="com.wangyg.springcloud.dao.PaymentDao">
<insert id="create" parameterType="com.wangyg.springcloud.entity.Payment" useGeneratedKeys="true" keyProperty="id">
insert into payment(serial) values(#{serial})
</insert>
<select id="getPaymentById" parameterType="java.lang.Long" resultType="com.wangyg.springcloud.entity.Payment">
select * from payment where id=#{id}
</select>
</mapper>
public interface PaymentDao {
int create(Payment payment);
Payment getPaymentById(@Param("id") Long id);
}
public interface PaymentService {
int create(Payment payment);
Payment getPaymentById(Long id);
}
public class PaymentServiceImpl implements PaymentService {
private PaymentDao paymentDao;
public int create(Payment payment) {
return paymentDao.create(payment);
}
public Payment getPaymentById(Long id) {
return paymentDao.getPaymentById(id);
}
}
public class PaymentController {
private PaymentService paymentService;
private String serverPort;
public CommonResult create( Long id) {
Payment payment = paymentService.getPaymentById(id);
if (payment != null) {
return new CommonResult(200, "查询成功,serverPort: " + serverPort, payment);
} else {
return new CommonResult(500, "没有对应记录,查询id:" + id, null);
}
}
public CommonResult create( Payment payment) {
int result = paymentService.create(payment);
if (result > 0) {
return new CommonResult(200, "插入成功,serverPort: " + serverPort, result);
} else {
return new CommonResult(500, "插入失败", result);
}
}
}
2. 修改订单服务,目录结构如下,这里是Ribbon使用的重点!!!
public class ApplicationContextConfig {
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
public class OrderController {
public static final String PAYMENT_URL = "http://CLOUD-PROVIDER-PAYMENT";
private RestTemplate restTemplate;
public CommonResult<Payment> getPayment( Long id) {
return restTemplate.getForObject(PAYMENT_URL + "/payment/get/" + id, CommonResult.class);
}
}
Q:Ribbon能干什么?
1. RoundRobinRule 轮询(默认策略)
算法:Rest接口请求次数 % 服务器集群总数量 = 实际调用服务器位置下标,每次服务重启Rest接口计数从1开始。
2. RandomRule 随机
我觉得这个不用解释。
3. RetryRule 重试
先按照轮询的策略获取服务,如果获取服务失败则在指定时间内会进行重试,获取可用的服务。
4. WeightedResponseTimeRule 时间加权
对轮询的扩展,响应速度越快的实例选择权重越大,越容易被选择。
5. BestAvailableRule 最优
会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务。
6. AvailabilityFilteringRule 可用过滤
先过滤掉故障实例,再选择并发较小的实例。
7. ZoneAvoidanceRule 这个不认识
复合判断server所在区域的性能和server的可用性选择服务器。
三、负载规则替换
如果不想使用轮询算法,我们可以更改负载的策略,首先需要新建个自己的规则类MyselfRule,但是这里有个坑 >>
官方文档明确给出警告,这个自定义配置类不能放在@ComponentScan所扫描的当前包及子包下,否则我们自定义的配置类会被所有的Ribbon客户端所共享,达不到特殊化定制的目的了。
熟悉springboot的应该知道,默认启动类的注解里就包含这个注解。
MyselfRule.java
public class MyselfRule {
public IRule myRule(){
return new RandomRule(); // 定义为随机
}
}
2. 主启动类添加注解
@RibbonClient(name = "CLOUD-PROVIDER-PAYMENT", configuration = MyselfRule.class)
3. 重启80订单服务,浏览器测试,可以看到访问的服务是没有规律的,说明我们自定义的规则生效了。
到这里,Ribbon的基本知识就学完了。
接下来开始学习下一篇:声明式服务调用组件Fegin的使用。