什么是負責均衡
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 文件內容
<?xml version="1.0" encoding="UTF-8"?>
<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><!-- springweb 啟動依賴 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- nacos 服務注冊發現(客戶端)依賴 --><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency><!-- nacos-config 配置中心依賴 --><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId></dependency><!--spring-cloud-dependencies 2020.0.0 版本不在默認加載bootstrap.yml 文件,如果需要加載bootstrap 文件需要手動添加依賴--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-bootstrap</artifactId></dependency><!--loadbalancer ,負載均衡,用來替代ribbon的組件 --><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-loadbalancer</artifactId></dependency></dependencies>
</project>
3、application.yml
server:port: 7001
spring:# 后面的bean會覆蓋前面相同名稱的beanmain:allow-bean-definition-overriding: true
4、bootstrap.yml
spring:application:name: ribbon-demoprofiles:active: yexindong_activecloud:nacos:discovery:server-addr: chn520.cn:8848 # 服務注冊中心地址namespace: public # 注冊到nacos的名稱空間,默認為publicconfig:prefix: yexindong_nacos_prefixfile-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
@EnableDiscoveryClient
public 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 {@Autowiredprivate 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;//這里不用寫Configuration
public class NacosSameClusterConfiguration{@BeanReactorLoadBalancer<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的配置信息@Resourceprivate NacosDiscoveryProperties nacosDiscoveryProperties;// loadbalancer 提供的訪問當前服務的名稱final String serviceId;// loadbalancer 提供的訪問的服務列表ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider;public NacosSameClusterWeightedRule(ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider, String serviceId) {this.serviceId = serviceId;this.serviceInstanceListSupplierProvider = serviceInstanceListSupplierProvider;}/*** 服務器調用負載均衡時調的放啊* 此處代碼內容與 RandomLoadBalancer 一致*/@Overridepublic 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<ServiceInstance> 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
后回車,顯示以下內容,表示測試成功