三、搭建springCloudAlibaba2021.1版本分布式微服务-springcloud loadbalancer负载均衡

发布时间:2022-07-11 18:54:31 作者:yexindonglai@163.com 阅读(1563)

什么是负责均衡

Spring Cloud LoadBalancer是一个客户端负载均衡器,类似于Ribbon,但是由于Ribbon已经进入维护模式,并且Ribbon 2并不与Ribbon 1相互兼容,所以Spring Cloud全家桶在Spring Cloud Commons项目中,添加了Spring cloud Loadbalancer作为新的负载均衡器,并且做了向前兼容,就算你的项目中继续用 Spring Cloud Netflix 套装(包括Ribbon,Eureka,Zuul,Hystrix等等)让你的项目中有这些依赖,你也可以通过简单的配置,把ribbon替换成Spring Cloud LoadBalancer。

ribbon与LoadBalance区别

SpringCloud原有的客户端负载均衡方案Ribbon已经被废弃,取而代之的是SpringCloud LoadBalancer。本文介绍SpringCloud LoadBalancer的搭建和测试验证过程。

  1. ribbon和loadbalancer都是springcloud的负载均衡组件
  2. ribbon是Netflix开源的基于HTTP和TCP等协议负载均衡组件,loadBalancer是SpringCloud自己写的,根据服务id获取负载均衡器rpc地址。
  3. Ribbon的使用需要代码里手动调用目标服务,loadBalancer底层原理是默认调用ribbon的实现客户端负载均衡
  4. Ribbon从2019年5月份后就不维护了,后期loadbalancer会成为主流,目前还是ribbon用的多。Loadbalancer支持ribbon。
  5. ribbon 提供7中默认的负载均衡策略,常见的常见都有覆盖,一般我们都是使用 ZoneAvoidanceRule 复合判断server所在区域的性能和server的可用性选择server
  6. ribbon 支持超时、懒加载处理、重试及其和 hystrix整合高级属性等
  7. 目前spring-cloud-loadbalancer 仅支持 重试操作的配置

2021.x 注意事项

Spring cloud alibaba 的Nacos最新版中,有以下几个问题

  1. nacos 2021 版本已经没有自带ribbon的整合,所以需要引入另一个支持的jar包 loadbalancer
  2. nacos 2021 版本已经取消了对ribbon的支持,所以无法通过修改Ribbon负载均衡的模式来实现nacos提供的负载均衡模式

开始搭建

1、创建工程

首下创建一个名为 spring-cloud-alibaba-2021-loadbalancer 的工程,继承spring-cloud-alibaba-2021父项目

2、pom.xml 文件内容
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  5. <parent>
  6. <artifactId>spring-cloud-alibaba-2021</artifactId>
  7. <groupId>org.example</groupId>
  8. <version>1.0-SNAPSHOT</version>
  9. </parent>
  10. <modelVersion>4.0.0</modelVersion>
  11. <artifactId>spring-cloud-alibaba-2021-loadbalancer</artifactId>
  12. <dependencies>
  13. <!-- springweb 启动依赖 -->
  14. <dependency>
  15. <groupId>org.springframework.boot</groupId>
  16. <artifactId>spring-boot-starter-web</artifactId>
  17. </dependency>
  18. <!-- nacos 服务注册发现(客户端)依赖 -->
  19. <dependency>
  20. <groupId>com.alibaba.cloud</groupId>
  21. <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
  22. </dependency>
  23. <!-- nacos-config 配置中心依赖 -->
  24. <dependency>
  25. <groupId>com.alibaba.cloud</groupId>
  26. <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
  27. </dependency>
  28. <!--spring-cloud-dependencies 2020.0.0 版本不在默认加载bootstrap.yml 文件,如果需要加载bootstrap 文件需要手动添加依赖-->
  29. <dependency>
  30. <groupId>org.springframework.cloud</groupId>
  31. <artifactId>spring-cloud-starter-bootstrap</artifactId>
  32. </dependency>
  33. <!--loadbalancer ,负载均衡,用来替代ribbon的组件 -->
  34. <dependency>
  35. <groupId>org.springframework.cloud</groupId>
  36. <artifactId>spring-cloud-starter-loadbalancer</artifactId>
  37. </dependency>
  38. </dependencies>
  39. </project>
3、application.yml
  1. server:
  2. port: 7001
  3. spring:
  4. # 后面的bean会覆盖前面相同名称的bean
  5. main:
  6. allow-bean-definition-overriding: true
4、bootstrap.yml
  1. spring:
  2. application:
  3. name: ribbon-demo
  4. profiles:
  5. active: yexindong_active
  6. cloud:
  7. nacos:
  8. discovery:
  9. server-addr: chn520.cn:8848 # 服务注册中心地址
  10. namespace: public # 注册到nacos的名称空间,默认为public
  11. config:
  12. prefix: yexindong_nacos_prefix
  13. file-extension: yaml # 指定yaml格式的配置, 必须要放到bootstrao.yml 才会生效,放到application下不会生效
  14. server-addr: chn520.cn:8848 #配置中心地址
  15. group: DEFAULT_GROUP
5、启动类 RibbonApp.java
  1. package com.alibaba.cloud;
  2. import org.springframework.boot.SpringApplication;
  3. import org.springframework.boot.autoconfigure.SpringBootApplication;
  4. import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
  5. @SpringBootApplication
  6. @EnableDiscoveryClient
  7. public class RibbonApp {
  8. public static void main(String[] args) {
  9. SpringApplication.run(RibbonApp.class, args);
  10. }
  11. }
6、控制层 RibbonController.java
  1. package com.alibaba.cloud.controller;
  2. import org.springframework.beans.factory.annotation.Autowired;
  3. import org.springframework.beans.factory.annotation.Value;
  4. import org.springframework.cloud.context.config.annotation.RefreshScope;
  5. import org.springframework.web.bind.annotation.RequestMapping;
  6. import org.springframework.web.bind.annotation.RestController;
  7. import org.springframework.web.client.RestTemplate;
  8. @RestController
  9. @RequestMapping("/ribbon")
  10. public class RibbonController {
  11. @Autowired
  12. private RestTemplate restTemplate;
  13. /**
  14. * 远程调用订单服务的接口
  15. * @return
  16. */
  17. @RequestMapping("/getRibbon")
  18. public String getRibbon(){
  19. // 以下2种方式都都可以调用远程服务
  20. String url = "http://order-demo/order/getOrderById";
  21. // String url = "http://localhost:8088/order/getOrderById";
  22. String res = restTemplate.getForObject(url, String.class);
  23. return "restTemplate 响应:"+res;
  24. }
  25. }
7、自定义负载均衡配置

SpringBeanConfiguration.java 内容如下

  1. package com.alibaba.cloud.config;
  2. import org.springframework.boot.web.client.RestTemplateBuilder;
  3. import org.springframework.cloud.client.loadbalancer.LoadBalanced;
  4. import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClients;
  5. import org.springframework.context.annotation.Bean;
  6. import org.springframework.context.annotation.Configuration;
  7. import org.springframework.web.client.RestTemplate;
  8. /**
  9. * @program: my-town
  10. * @author: 洛天
  11. * @create: 2021-12-13 16:27
  12. **/
  13. @Configuration
  14. // 在这里配置我们自定义的LoadBalancer策略,注:这里的类为注入Bean的类,而非负载均衡的实现类
  15. @LoadBalancerClients(defaultConfiguration = {NacosSameClusterConfiguration.class})
  16. public class SpringBeanConfiguration {
  17. @Bean
  18. @LoadBalanced // 开启负载均衡,必须的
  19. public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder){
  20. return restTemplateBuilder.build();
  21. }
  22. }

NacosSameClusterConfiguration.java 调用负载均衡算法,选取其中的一个节点

  1. package com.alibaba.cloud.config;
  2. import org.springframework.cloud.client.ServiceInstance;
  3. import org.springframework.cloud.loadbalancer.core.ReactorLoadBalancer;
  4. import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
  5. import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
  6. import org.springframework.context.annotation.Bean;
  7. import org.springframework.core.env.Environment;
  8. //这里不用写Configuration
  9. public class NacosSameClusterConfiguration{
  10. @Bean
  11. ReactorLoadBalancer<ServiceInstance> randomLoadBalancer(Environment environment,
  12. LoadBalancerClientFactory loadBalancerClientFactory) {
  13. // 获取远程调用的服务名称
  14. String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
  15. // 返回内容为自定义负载均衡的配置类
  16. return new NacosSameClusterWeightedRule(loadBalancerClientFactory
  17. .getLazyProvider(name, ServiceInstanceListSupplier.class),
  18. name);
  19. }
  20. }

NacosSameClusterWeightedRule.java 自定义负载均衡的实现类

  1. package com.alibaba.cloud.config;
  2. import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
  3. import org.springframework.beans.factory.ObjectProvider;
  4. import org.springframework.cloud.client.ServiceInstance;
  5. import org.springframework.cloud.client.loadbalancer.DefaultResponse;
  6. import org.springframework.cloud.client.loadbalancer.EmptyResponse;
  7. import org.springframework.cloud.client.loadbalancer.Request;
  8. import org.springframework.cloud.client.loadbalancer.Response;
  9. import org.springframework.cloud.loadbalancer.core.NoopServiceInstanceListSupplier;
  10. import org.springframework.cloud.loadbalancer.core.ReactorServiceInstanceLoadBalancer;
  11. import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
  12. import reactor.core.publisher.Mono;
  13. import javax.annotation.Resource;
  14. import java.util.List;
  15. import java.util.Random;
  16. // 自定义负载均衡实现需要实现 ReactorServiceInstanceLoadBalancer 接口 以及重写choose方法
  17. public class NacosSameClusterWeightedRule implements ReactorServiceInstanceLoadBalancer {
  18. // 注入当前服务的nacos的配置信息
  19. @Resource
  20. private NacosDiscoveryProperties nacosDiscoveryProperties;
  21. // loadbalancer 提供的访问当前服务的名称
  22. final String serviceId;
  23. // loadbalancer 提供的访问的服务列表
  24. ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider;
  25. public NacosSameClusterWeightedRule(ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider, String serviceId) {
  26. this.serviceId = serviceId;
  27. this.serviceInstanceListSupplierProvider = serviceInstanceListSupplierProvider;
  28. }
  29. /**
  30. * 服务器调用负载均衡时调的放啊
  31. * 此处代码内容与 RandomLoadBalancer 一致
  32. */
  33. @Override
  34. public Mono<Response<ServiceInstance>> choose(Request request) {
  35. ServiceInstanceListSupplier supplier = this.serviceInstanceListSupplierProvider.getIfAvailable(NoopServiceInstanceListSupplier::new);
  36. Mono<Response<ServiceInstance>> map1 = supplier.get(request).next().map((list) -> {
  37. Response<ServiceInstance> serviceInstanceResponse = this.getInstanceResponse(list);
  38. ;
  39. return serviceInstanceResponse;
  40. });
  41. return map1;
  42. }
  43. /**
  44. * 对负载均衡的服务进行筛选的方法
  45. * 自定义
  46. * 此处的 instances 实例列表 只会提供健康的实例 所以不需要担心如果实例无法访问的情况
  47. */
  48. private Response<ServiceInstance> getInstanceResponse(List<ServiceInstance> instances) {
  49. if (instances.isEmpty()) {
  50. return new EmptyResponse();
  51. }
  52. // 获取当前服务所在的集群名称
  53. // String currentClusterName = nacosDiscoveryProperties.getClusterName();
  54. // 过滤在同一集群下注册的服务 根据集群名称筛选的集合
  55. // List<ServiceInstance> sameClusterNameInstList = instances.stream().filter(i-> StringUtils.equals(i.getMetadata().get("nacos.cluster"),currentClusterName)).collect(Collectors.toList());
  56. // ServiceInstance sameClusterNameInst;
  57. // if (sameClusterNameInstList.isEmpty()) {
  58. // // 如果为空,则根据权重直接过滤所有服务列表
  59. // sameClusterNameInst = getHostByRandomWeight(instances);
  60. // } else {
  61. // // 如果不为空,则根据权重直接过滤所在集群下的服务列表
  62. // sameClusterNameInst = getHostByRandomWeight(sameClusterNameInstList);
  63. // }
  64. // 随机选择一个节点
  65. int size = instances.size();
  66. int index = new Random().nextInt(size); // 如果size为2,生成 0-1之间的随机数;如果为10,生成0-9之间的随机数
  67. System.out.println("生成的随机数为:" + index);
  68. ServiceInstance serviceInstance = instances.get(index);
  69. return new DefaultResponse(serviceInstance);
  70. }
  71. }
8、父工程加入 module

在 父项目(spring-cloud-alibaba-2021)加入以下内容

  1. <modules>
  2. <module>spring-cloud-alibaba-2021-loadbalancer</module>
  3. </modules>
9、搭建完成

此时工程目录如下图

测试

在浏览器输入http://127.0.0.1:7001/ribbon/getRibbon后回车,显示以下内容,表示测试成功

关键字SpringCloud