三、搭建springCloudAlibaba2021.1版本分布式微服务-springcloud loadbalancer负载均衡
什么是负责均衡
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的搭建和测试验证过程。
- ribbon和loadbalancer都是springcloud的负载均衡组件
- ribbon是Netflix开源的基于HTTP和TCP等协议负载均衡组件,loadBalancer是SpringCloud自己写的,根据服务id获取负载均衡器rpc地址。
- Ribbon的使用需要代码里手动调用目标服务,loadBalancer底层原理是默认调用ribbon的实现客户端负载均衡
- Ribbon从2019年5月份后就不维护了,后期loadbalancer会成为主流,目前还是ribbon用的多。Loadbalancer支持ribbon。
- ribbon 提供7中默认的负载均衡策略,常见的常见都有覆盖,一般我们都是使用 ZoneAvoidanceRule 复合判断server所在区域的性能和server的可用性选择server
- ribbon 支持超时、懒加载处理、重试及其和 hystrix整合高级属性等
- 目前
spring-cloud-loadbalancer
仅支持 重试操作的配置
2021.x 注意事项
Spring cloud alibaba 的Nacos最新版中,有以下几个问题
- nacos 2021 版本已经没有自带ribbon的整合,所以需要引入另一个支持的jar包 loadbalancer
- nacos 2021 版本已经取消了对ribbon的支持,所以无法通过修改Ribbon负载均衡的模式来实现nacos提供的负载均衡模式
开始搭建
1、创建工程
首下创建一个名为 spring-cloud-alibaba-2021-loadbalancer
的工程,继承spring-cloud-alibaba-2021
父项目
2、pom.xml 文件内容
<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\"> <parent> <artifactId>spring-cloud-alibaba-2021</artifactId> <groupId>org.example</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>spring-cloud-alibaba-2021-loadbalancer</artifactId> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-bootstrap</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-loadbalancer</artifactId> </dependency> </dependencies></project>
3、application.yml
server: port: 7001spring: # 后面的bean会覆盖前面相同名称的bean main: allow-bean-definition-overriding: true
4、bootstrap.yml
spring: application: name: ribbon-demo profiles: active: yexindong_active cloud: nacos: discovery: server-addr: chn520.cn:8848 # 服务注册中心地址 namespace: public # 注册到nacos的名称空间,默认为public config: prefix: yexindong_nacos_prefix file-extension: yaml # 指定yaml格式的配置, 必须要放到bootstrao.yml 才会生效,放到application下不会生效 server-addr: chn520.cn:8848 #配置中心地址 group: DEFAULT_GROUP
5、启动类 RibbonApp.java
package com.alibaba.cloud;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cloud.client.discovery.EnableDiscoveryClient;@SpringBootApplication@EnableDiscoveryClientpublic class RibbonApp { public static void main(String[] args) { SpringApplication.run(RibbonApp.class, args); }}
6、控制层 RibbonController.java
package com.alibaba.cloud.controller;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.beans.factory.annotation.Value;import org.springframework.cloud.context.config.annotation.RefreshScope;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import org.springframework.web.client.RestTemplate;@RestController@RequestMapping(\"/ribbon\")public class RibbonController { @Autowired private RestTemplate restTemplate; /** * 远程调用订单服务的接口 * @return */ @RequestMapping(\"/getRibbon\") public String getRibbon(){ // 以下2种方式都都可以调用远程服务 String url = \"http://order-demo/order/getOrderById\";// String url = \"http://localhost:8088/order/getOrderById\"; String res = restTemplate.getForObject(url, String.class); return \"restTemplate 响应:\"+res; }}
7、自定义负载均衡配置
SpringBeanConfiguration.java
内容如下
package com.alibaba.cloud.config;import org.springframework.boot.web.client.RestTemplateBuilder;import org.springframework.cloud.client.loadbalancer.LoadBalanced;import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClients;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.web.client.RestTemplate;/** * @program: my-town * @author: 洛天 * @create: 2021-12-13 16:27 **/@Configuration// 在这里配置我们自定义的LoadBalancer策略,注:这里的类为注入Bean的类,而非负载均衡的实现类@LoadBalancerClients(defaultConfiguration = {NacosSameClusterConfiguration.class})public class SpringBeanConfiguration { @Bean @LoadBalanced // 开启负载均衡,必须的 public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder){ return restTemplateBuilder.build(); }}
NacosSameClusterConfiguration.java
调用负载均衡算法,选取其中的一个节点
package com.alibaba.cloud.config;import org.springframework.cloud.client.ServiceInstance;import org.springframework.cloud.loadbalancer.core.ReactorLoadBalancer;import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;import org.springframework.context.annotation.Bean;import org.springframework.core.env.Environment;//这里不用写Configurationpublic class NacosSameClusterConfiguration{ @Bean ReactorLoadBalancer<ServiceInstance> randomLoadBalancer(Environment environment, LoadBalancerClientFactory loadBalancerClientFactory) { // 获取远程调用的服务名称 String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME); // 返回内容为自定义负载均衡的配置类 return new NacosSameClusterWeightedRule(loadBalancerClientFactory .getLazyProvider(name, ServiceInstanceListSupplier.class), name); }}
NacosSameClusterWeightedRule.java
自定义负载均衡的实现类
package com.alibaba.cloud.config;import com.alibaba.cloud.nacos.NacosDiscoveryProperties;import org.springframework.beans.factory.ObjectProvider;import org.springframework.cloud.client.ServiceInstance;import org.springframework.cloud.client.loadbalancer.DefaultResponse;import org.springframework.cloud.client.loadbalancer.EmptyResponse;import org.springframework.cloud.client.loadbalancer.Request;import org.springframework.cloud.client.loadbalancer.Response;import org.springframework.cloud.loadbalancer.core.NoopServiceInstanceListSupplier;import org.springframework.cloud.loadbalancer.core.ReactorServiceInstanceLoadBalancer;import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;import reactor.core.publisher.Mono;import javax.annotation.Resource;import java.util.List;import java.util.Random;// 自定义负载均衡实现需要实现 ReactorServiceInstanceLoadBalancer 接口 以及重写choose方法public class NacosSameClusterWeightedRule implements ReactorServiceInstanceLoadBalancer { // 注入当前服务的nacos的配置信息 @Resource private NacosDiscoveryProperties nacosDiscoveryProperties; // loadbalancer 提供的访问当前服务的名称 final String serviceId; // loadbalancer 提供的访问的服务列表 ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider; public NacosSameClusterWeightedRule(ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider, String serviceId) { this.serviceId = serviceId; this.serviceInstanceListSupplierProvider = serviceInstanceListSupplierProvider; } /** * 服务器调用负载均衡时调的放啊 * 此处代码内容与 RandomLoadBalancer 一致 */ @Override public Mono<Response<ServiceInstance>> choose(Request request) { ServiceInstanceListSupplier supplier = this.serviceInstanceListSupplierProvider.getIfAvailable(NoopServiceInstanceListSupplier::new); Mono<Response<ServiceInstance>> map1 = supplier.get(request).next().map((list) -> { Response<ServiceInstance> serviceInstanceResponse = this.getInstanceResponse(list); ; return serviceInstanceResponse; }); return map1; } /** * 对负载均衡的服务进行筛选的方法 * 自定义 * 此处的 instances 实例列表 只会提供健康的实例 所以不需要担心如果实例无法访问的情况 */ private Response<ServiceInstance> getInstanceResponse(List<ServiceInstance> instances) { if (instances.isEmpty()) { return new EmptyResponse(); } // 获取当前服务所在的集群名称// String currentClusterName = nacosDiscoveryProperties.getClusterName(); // 过滤在同一集群下注册的服务 根据集群名称筛选的集合// List sameClusterNameInstList = instances.stream().filter(i-> StringUtils.equals(i.getMetadata().get(\"nacos.cluster\"),currentClusterName)).collect(Collectors.toList());// ServiceInstance sameClusterNameInst;// if (sameClusterNameInstList.isEmpty()) {// // 如果为空,则根据权重直接过滤所有服务列表// sameClusterNameInst = getHostByRandomWeight(instances);// } else {// // 如果不为空,则根据权重直接过滤所在集群下的服务列表// sameClusterNameInst = getHostByRandomWeight(sameClusterNameInstList);// } // 随机选择一个节点 int size = instances.size(); int index = new Random().nextInt(size); // 如果size为2,生成 0-1之间的随机数;如果为10,生成0-9之间的随机数 System.out.println(\"生成的随机数为:\" + index); ServiceInstance serviceInstance = instances.get(index); return new DefaultResponse(serviceInstance); }}
8、父工程加入 module
在 父项目(spring-cloud-alibaba-2021)加入以下内容
<modules> <module>spring-cloud-alibaba-2021-loadbalancer</module> </modules>
9、搭建完成
此时工程目录如下图
测试
在浏览器输入http://127.0.0.1:7001/ribbon/getRibbon
后回车,显示以下内容,表示测试成功