Spring Cloud Netflix 02 Ribbon

4 Ribbon

4.1 是什么

  • Spring Cloud Ribbon是基于Netflix Ribbon实现的一套客户端负载均衡的工具。
  • 简单的说,Ribbon是Netflix发布的开源项目,主要功能是提供客户端的软件负载均衡算法,将NetFlix的中间层服务连接在一起。Ribbon的客户端组件提供一系列完整的配置项如:连接超时、重试等等。简单的说,就是在配置文件中列出LoadBalancer(简称LB:负载均衡)后面所有的机器,Ribbon会自动的帮助你基于某种规则(如简单轮询,随机连接等等)去连接这些机器。我们也很容易使用Ribbon实现自定义的负载均衡算法

Load Balance介绍

  • 负载均衡(Load Balance),在微服务或分布式集群中经常用的一种应用。
    负载均衡简单的说就是将用户的请求平摊的分配到多个服务上,从而达到系统的HA(高可用)。常见的负载均衡软件有 Nginx,Lvs 等等
  • 简单分类:集中式和进程式
    • 集中式
      • 在服务方和消费方中间有一个独立的负载均衡设施。所有的请求都先访问这个设施,由这个设施来完成对请求的分发。比如说Nginx
    • 进程式
      • 进程式负载均衡则将负载均衡集成在消费方中,他获取所有可用的服务地址,然后通过负载均衡算法来决定方法哪一个服务

4.2 集成Ribbon

1)pom.xml

  • 由于新版的Eureka依赖中已经包含了Ribbon组件,所以我们无需再导入Ribbon的依赖

    image-20210301164428887

  • 注意:这里如果重复导入,那么要保证Eureka和Ribbon的版本一致

2)application.yaml

server:
  port: 80

spring:
  application:
    name: consumer-ribbon-80
eureka:
  client:
    register-with-eureka: false # 作为消费者,不需要向注册中心注册自己
    fetch-registry: true # 但是作为消费者,需要从注册中心中拉去服务信息
    service-url:
      defaultZone: http://eureka7000:7000/eureka/, http://eureka7001:7001/eureka/, http://eureka7002:7002/eureka/, http://eureka7003:7003/eureka/, http://eureka7004:7004/eureka/

3)启动类

@SpringBootApplication
@EnableEurekaClient // 我们要启用Eureka来获取可用的服务
public class RibbonConsumer_80 {
    public static void main(String[] args) {
        SpringApplication.run(RibbonConsumer_80.class, args);
    }
}

4)负载均衡

@Configuration
public class RestTemplateConfig {

    @LoadBalanced	// 一个注解就可以实现默认情况下的负载均衡
    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }

}

5)controller

@RestController
@RequestMapping("/consumer")
@Slf4j
public class ConsumerController {

    @Autowired
    private RestTemplate template;

    // private static final String PROVIDER_URL_PREFIX = "http://127.0.0.1:8001/provider";
    // 因为使用了负载均衡,所以访问服务的地址要从一个常量变成一个服务地址
    private static final String PROVIDER_URL_PREFIX = "http://PROVIDER-EUREKA-8001";

    @GetMapping("/add")
    public Message add(Department department) {
        return template.postForObject(PROVIDER_URL_PREFIX + "/provider/insert", department, Message.class);
    }

    @GetMapping("/get/{id}")
    public Message getDepartmentById(@PathVariable Long id) {
        return template.getForObject(PROVIDER_URL_PREFIX + "/provider/get/" + id, Message.class);
    }

    @GetMapping("/get/all")
    public Message getDepartmentList() {
        return template.getForObject(PROVIDER_URL_PREFIX + "/provider/get/all", Message.class);
    }
}

6)测试

image-20210301165023623

image-20210301165123084

4.3 负载均衡

1)改造消息传递组件

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Message implements Serializable {

    private int code;
    private String source;
    private String msg;
    private Object data;


    public Message(int code, String source, String msg) {
        this(code, msg, source, null);
    }

}

2)改造controller

@RestController
@RequestMapping("/provider")
@Slf4j
public class DepartmentController {

    @Autowired
    private DepartmentService service;

    @Value("${spring.application.name}")
    private String source;

    @PostMapping("/insert")
    public Message insert(@RequestBody String name, HttpServletRequest request) {
        log.info("host:" + request.getRemoteAddr() + " try to insert a department, name: " + name);
        Department department = new Department();
        department.setName(name);
        Department res = service.insertDepartment(name);
        if (res != null && res.getId() != null) {
            log.info("insert successes, new department = " + res);
            return new Message(200, source, "插入成功", res);
        } else {
            log.info("insert failed");
            return new Message(400, source, "插入失败", res);
        }
    }

    @GetMapping("/get/{id}")
    public Message getDepartmentById(@PathVariable("id") Long id, HttpServletRequest request) {
        log.info("host:" + request.getRemoteAddr() + " try to get a department by id, id = " + id);
        Department res = service.getDepartmentById(id);
        if (res != null) {
            log.info("search successes, department = " + res);
            return new Message(200, source, "查找成功", res);
        } else {
            log.info("search failed");
            return new Message(400, source, "查找失败");
        }
    }

    @GetMapping("/get/all")
    public Message getAllDepartment(HttpServletRequest request) {
        log.info(request.getRemoteAddr() + " try to get department list");
        List<Department> list = service.getDepartmentList();
        if (list.size() > 0) {
            log.info("search success, + department nums = " + list.size());
            return new Message(200, source, "查找成功", list);
        } else {
            log.info("search failed");
            return new Message(400, source, "查找失败");
        }
    }
}@RestController
@RequestMapping("/provider")
@Slf4j
public class DepartmentController {

    @Autowired
    private DepartmentService service;

    // 新增属性
    @Value("${eureka.instance.instance-id}")
    private String source;

    @PostMapping("/insert")
    public Message insert(@RequestBody String name, HttpServletRequest request) {
        log.info("host:" + request.getRemoteAddr() + " try to insert a department, name: " + name);
        Department department = new Department();
        department.setName(name);
        Department res = service.insertDepartment(name);
        if (res != null && res.getId() != null) {
            log.info("insert successes, new department = " + res);
            return new Message(200, source, "插入成功", res);
        } else {
            log.info("insert failed");
            return new Message(400, source, "插入失败", res);
        }
    }

    @GetMapping("/get/{id}")
    public Message getDepartmentById(@PathVariable("id") Long id, HttpServletRequest request) {
        log.info("host:" + request.getRemoteAddr() + " try to get a department by id, id = " + id);
        Department res = service.getDepartmentById(id);
        if (res != null) {
            log.info("search successes, department = " + res);
            return new Message(200, source, "查找成功", res);
        } else {
            log.info("search failed");
            return new Message(400, source, "查找失败");
        }
    }

    @GetMapping("/get/all")
    public Message getAllDepartment(HttpServletRequest request) {
        log.info(request.getRemoteAddr() + " try to get department list");
        List<Department> list = service.getDepartmentList();
        if (list.size() > 0) {
            log.info("search success, + department nums = " + list.size());
            return new Message(200, source, "查找成功", list);
        } else {
            log.info("search failed");
            return new Message(400, source, "查找失败");
        }
    }
}

3)增加provider

4)测试

image-20210301184512028

image-20210301184524395

image-20210301184533668

4.4 IRule

  • Ribbon到底干了什么?

    • 架构图

      image-20210301184739816

    • 总结:Ribbon就干了两件事,查询可用服务,以及负载均衡请求

  • Ribbon中的负载均衡算法

    算法 介绍
    AvailabilityFilteringRule 排除高并发和掉线的之后的轮询
    BestAvailableRule 在能响应的服务器中选择并发数量最小的
    RandomRule 随机选择
    RetryRule 重试
    RoundRobinRule 轮询
    WeightedResponseTimeRule 平均响应时间最快
    ZoneAvoidanceRule 基于区域和可用性进行选择
posted @ 2021-04-12 17:06  PrimaBruceXu  阅读(49)  评论(0编辑  收藏  举报