vlambda博客
学习文章列表

负载均衡组件Ribbon的实现

Ribbon是什么?
  • SpringCloud Ribbon是基于Netflix Ribbon实现的一套客户端负载均衡的工具。

  • Ribbon是Netflix发布的开源项目,主要功能是提供客户端的软件负载均衡算法和服务调用。

    题外话:springcloud一直想把ribbon替换成自己的组件LoadBalancer,但是无奈ribbon属实优秀,且已大规模部署到生产环境中,一时半会儿替代不了,不过未来的趋势可能会变,这也说不好,目前主流还是ribbon。

LB负载均衡(Load Balance)是什么?
  • 简单的说就是将用户的请求平摊的分配到多个服务上,从而达到系统的HA(高可用)。
  • 常见的负载均衡有软件Nginx,LVS,硬件F5等。

Ribbon本地LB与Nginx服务端LB区别
  1. Nginx是服务器负载均衡,客户端所有请求都会交给nginx,然后由nginx实现转发请求。即LB是由服务端实现的。属于集中式LB。

  2. Ribbon是本地负载均衡,在调用微服务接口的时候,会在注册中心上获取注册信息服务列表之后缓存到JVM本地,从而在本地实现RPC远程服务调用。属于进程内LB。

扩展知识 >>

    集中式LB:即在服务的消费方和提供方之间使用独立的LB设施(可以是硬件,如F5,也可以是软件,如nginx),由该设施负责把访问请求通过某种策略转发至服务提供方。

    在2018年学习springcloud,使用Ribbon需添加相关依赖,现在不需要了,因为Eureka包里就提供了Ribbon的依赖包。

    我们现在有支付服务payment和订单服务order,我需要实现订单服务调用支付服务,但是这是两个独立的项目,想要实现服务之间调用,在学习下一篇文章之前,目前只能使用RestTemplate( 这个我认为大家应该都用过), spring提供的基于httpClient封装的远程访问服务模版类。

一、Ribbon的简单使用
1. 编写支付服务各个业务类的内容,两个支付服务完全一样(除了端口),直接复制就行
    PaymentMapper.xml
<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" ><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>
    PaymentDao.java
@Mapperpublic interface PaymentDao { int create(Payment payment); Payment getPaymentById(@Param("id") Long id);}
    PaymentService.java
public interface PaymentService { int create(Payment payment); Payment getPaymentById(Long id);}
    PaymentServiceImpl.java
@Servicepublic class PaymentServiceImpl implements PaymentService { @Resource  private PaymentDao paymentDao;   @Override public int create(Payment payment) { return paymentDao.create(payment);  } @Override public Payment getPaymentById(Long id) { return paymentDao.getPaymentById(id); }}
    PaymentController.java
@RestController@Slf4jpublic class PaymentController { @Resource  private PaymentService paymentService; @Value("${server.port}") private String serverPort;
@GetMapping("/payment/get/{id}") public CommonResult create(@PathVariable("id") 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); }  } @PostMapping("/payment/create") public CommonResult create(@RequestBody 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使用的重点!!!

负载均衡组件Ribbon的实现

    ApplicationContextConfig.java
@Configurationpublic class ApplicationContextConfig { @Bean @LoadBalanced public RestTemplate restTemplate(){ return new RestTemplate(); }}
    OrderController.java
@RestController@Slf4jpublic class OrderController {  public static final String PAYMENT_URL = "http://CLOUD-PROVIDER-PAYMENT"; @Resource private RestTemplate restTemplate;
@GetMapping("/consumer/payment/get/{id}") public CommonResult<Payment> getPayment(@PathVariable("id") Long id) { return restTemplate.getForObject(PAYMENT_URL + "/payment/get/" + id, CommonResult.class); }}
3. 按顺序启动8761,8762,8001,8002,80五个服务,在地址栏测试,可以看到订单服务轮询访问两个支付服务(看端口的变化)
负载均衡组件Ribbon的实现

Q:Ribbon能干什么?

负载均衡 + RestTemplate调用
二、Ribbon自带的负载均衡算法

1. RoundRobinRule 轮询(默认策略)

    算法:Rest接口请求次数 % 服务器集群总数量 = 实际调用服务器位置下标,每次服务重启Rest接口计数从1开始

2. RandomRule 随机

    我觉得这个不用解释。

3. RetryRule 重试

    先按照轮询的策略获取服务,如果获取服务失败则在指定时间内会进行重试,获取可用的服务。

4. WeightedResponseTimeRule 时间加权

    对轮询的扩展,响应速度越快的实例选择权重越大,越容易被选择。

5. BestAvailableRule 最优

    会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务。

6. AvailabilityFilteringRule 可用过滤

    先过滤掉故障实例,再选择并发较小的实例。

7. ZoneAvoidanceRule 这个不认识

    复合判断server所在区域的性能和server的可用性选择服务器。

三、负载规则替换

    如果不想使用轮询算法,我们可以更改负载的策略,首先需要新建个自己的规则类MyselfRule,但是这里有个坑 >>

    官方文档明确给出警告,这个自定义配置类不能放在@ComponentScan所扫描的当前包及子包下,否则我们自定义的配置类会被所有的Ribbon客户端所共享,达不到特殊化定制的目的了。

    熟悉springboot的应该知道,默认启动类的注解里就包含这个注解。

负载均衡组件Ribbon的实现

1. 所以我们应该在外面新建一个包

    MyselfRule.java

@Configurationpublic class MyselfRule { @Bean public IRule myRule(){ return new RandomRule(); // 定义为随机 }}

2. 主启动类添加注解

@RibbonClient(name = "CLOUD-PROVIDER-PAYMENT", configuration = MyselfRule.class)

3. 重启80订单服务,浏览器测试,可以看到访问的服务是没有规律的,说明我们自定义的规则生效了。


到这里,Ribbon的基本知识就学完了。


接下来开始学习下一篇:声明式服务调用组件Fegin的使用。