Ribbon實現負載均衡
負載均衡,英文名稱爲Load Balance,其含義就是指將負載(工作任務)進行平衡、分攤到多個操作單元上進行運行,例如FTP服務器、Web服務器、企業核心應用服務器和其它主要任務服務器等,從而協同完成工作任務。
- 導入pom依賴
單獨導入
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
<version>2.2.1.RELEASE</version>
</dependency>
整合依賴spring-cloud-starter-netflix-eureka-client裏面也包含了ribbon的依賴
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<version>2.2.1.RELEASE</version>
</dependency>
- 在主啓動類所在包的上一級創建一個包,用來創建自定義負載均衡策略類
書寫自定義負載均衡策略類,先繼承AbstractLoadBalancerRule抽象類,該類是IRule接口的一個抽象實現類,繼承它,然後自定義自己的負載均衡策略,注入spring容器即可覆蓋原有的輪詢策略。
public class HcodeRule extends AbstractLoadBalancerRule {
//每個服務,訪問5次,然後循環所有服務
private AtomicInteger total = new AtomicInteger(0); //服務被調用的次數
private AtomicInteger currentIndex = new AtomicInteger(0); //當前被調用服務的下標
public Server choose(ILoadBalancer lb, Object key) {
if (lb == null) {
return null;
}
Server server = null;
while (server == null) {
if (Thread.interrupted()) {
return null;
}
//獲得活着的服務
List<Server> upList = lb.getReachableServers();
//獲得全部服務
List<Server> allList = lb.getAllServers();
int serverCount = allList.size();
if (serverCount == 0) {
return null;
}
// //生成區間隨機數
// int index = chooseRandomInt(serverCount);
// server = upList.get(index); //從或者的服務中隨機獲取一個
/*============================================*/
if(total.get()<5){
server = upList.get(currentIndex.get());
getAndIncrement(total);
}else{
total.set(0);
getAndIncrement(currentIndex);
if(currentIndex.get()>=upList.size()){
currentIndex.set(0);
}
server = upList.get(currentIndex.get());
getAndIncrement(total);
}
/*==============================================*/
if (server == null) {
Thread.yield();
continue;
}
if (server.isAlive()) {
return (server);
}
// Shouldn't actually happen.. but must be transient or a bug.
server = null;
Thread.yield();
}
return server;
}
//自旋鎖防止高併發情況數據不一致性
private final int getAndIncrement(AtomicInteger atomicInteger){
int current;
int next;
do {
current = atomicInteger.get();
next = current >= 2147483647 ? 0 : current + 1;
}while (!atomicInteger.compareAndSet(current,next)); //第一個參數是期望值,第二個參數是修改值是
return next;
}
protected int chooseRandomInt(int serverCount) {
return ThreadLocalRandom.current().nextInt(serverCount);
}
@Override
public void initWithNiwsConfig(IClientConfig clientConfig) {
// TODO Auto-generated method stub
}
@Override
public Server choose(Object key) {
return choose(getLoadBalancer(), key);
}
}
再將該策略類注入IOC容器,當然如果不想寫自定義策略類,那就使用原有的IRule的實現類,將其注入IOC容器即可.下面兩種二選一
@Configuration
public class MyRule {
@Bean //使用自定義的
public IRule HcodeRule(){
return new HcodeRule();
}
//============================================================
//AvailabilityFilteringRule : 先過濾掉出現故障的服務器,對剩下的服務進行輪詢
//RoundRobinRule 輪詢 默認設置
//RandomRule 隨機
//WeightedResponseTimeRule 權重
//RetryRule:先按照輪詢獲取,若獲取服務失敗,則在指定時間內進行重試
//隨機訪問,只要將實現類注入到ICO容器,即可覆蓋
@Bean //使用原有的實現類
public IRule myRule(){
return new RandomRule();
}
}
- 主啓動類上加上註解,讓消費端服務能去使用自定義的負載均衡策略
@SpringBootApplication
@EnableEurekaClient
//在微服務啓動的時候就能去加載我們自定義的負載均衡Ribbon類,name爲微服務提供者註冊到註冊中心的服務名
@RibbonClient(name="SPRINGCLOUD-PROVIDER-DEPT",configuration = MyRule.class)
public class DeptConsumer_80 {
public static void main(String[] args) {
SpringApplication.run(DeptConsumer_80.class,args);
}
}
相關概念理解
- 服務熔斷:當某個服務提供者出現問題(出現運行時無法處理的異常或者某些操作時間過長),卡死了,不能讓用戶一直等待,需要調用備用的響應方法向調用方返回特定響應,防止服務鏈路某一微服務模塊卡死,導致整體服務出現雪崩。(發生熔斷一般是某個微服務模塊,也就是微服務提供者)
- 服務降級:服務降級一般也是有多種情況,訪問量過大,微服務出現異常,或者微服務響應超時等等,一般服務降級觸發熔斷都在消費者端設置,當某個服務不可用時,直接返回備用響應。
- 服務限流:也是服務降級的一種方式,限流,比如秒殺場景,不能訪問用戶瞬間都訪問服務器,限制一次只可以有多少請求。
Hystrix 實現服務熔斷和服務降級
- 微服務提供者導入pom依賴
單獨導入依賴 org.springframework.cloud:spring-cloud-starter-hystrix:2.2.1.RELEASE
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
<version>2.2.1.RELEASE</version>
</dependency>
使用eureka整合客戶端pom就不用再導入單獨的hystrix
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
- 微服務提供者的controller層
@RestController
@DefaultProperties(defaultFallback = "Global_FallbackMethod") //沒有配置專屬熔斷器方法,就走全局熔斷器
public class TestController {
@HystrixCommand //沒加特定方法,走全局
@PostMapping("/global")
public String test1(){
return "(●'◡'●)全局熔斷器方法未觸發";
}
//全局響應Fallback方法 服務降級觸發熔斷
public Object Global_FallbackMethod(){
return "┭┮﹏┭┮全局熔斷器方法觸發了,嗚嗚嗚!";
}
//斷路器
@HystrixCommand( fallbackMethod = "CircuitBreaker_fallback" , commandProperties = {
@HystrixProperty(name = "circuitBreaker.enabled", value = "true"),// 是否開啓斷路器
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "10"),// 請求次數
@HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "10000"), //時間窗口期,失敗後經過多久嘗試恢復
@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "60") //失敗率到達多少後跳閘
})
@GetMapping("/local")
public String test2(){
return "(●'◡'●)特定斷路器方法未觸發~";
}
public String CircuitBreaker_fallback(){ //參數可以跟原接口方法參數一致
return "┭┮﹏┭┮特定限制方法熔斷器觸發了,嗚嗚嗚~";
}
}
- 微服務提供者的主啓動類加上開啓斷路器的註解
@EnableCircuitBreaker //添加對服務熔斷的支持
- 客戶端導入的所需依賴跟提供者差不多,使用openfeign進行服務調用通信。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
然後配置yml,讓feign支持hystrix
feign:
hystrix:
enabled: true #如果處理自身的容錯就開啓。開啓方式與生產端不一樣。
然後再服務層接口加上註解。
@FeignClient(value = "hystrix-test-provider",fallback = DeptFallbackService.class) //服務名,備用的響應類
@Component
public interface testService {
@GetMapping("/global")
public String test1();
}
兜底用的備用響應類
@Component // 服務降級
public class DeptFallbackService implements testService{
public String test1() {
return "┭┮﹏┭┮該服務被降級了,目前無法使用";
}
}
最後,主啓動類添加@EnableHystrix
@EnableHystrix
Sentinel實現服務熔斷,降級,限流
老樣子,hystrix也是停止更新了,所以換新的纔是王道~況且阿里巴巴的sentinel就是繼承hystrix,兩者挺像的。
- 官網下載:https://github.com/alibaba/Sentinel/releases
- 特性:
- 核心庫(Java 客戶端)不依賴任何框架/庫,能夠運行於所有 Java 運行時環境,同時對 Dubbo / Spring Cloud 等框架也有較好的支持。
- 控制檯(Dashboard)基於 Spring Boot 開發,打包後可以直接運行,不需要額外的 Tomcat 等應用容器。
下載好jar包sentinel-dashboard-1.7.0.jar後,直接命令行運行即可~
注意:該boot服務默認爲8080端口號。啓動後,訪問http://localhost:8080,賬號密碼都是sentinel。
服務提供者
- 導入pom
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
- 配置yml
server:
port: 8666
spring:
application:
name: cloudalibaba-sentinel-service
cloud:
nacos:
discovery:
server-addr: localhost:8848 # nacos註冊中心的地址
sentinel:
transport:
dashboard: localhost:8080 # sentinel的地址
port: 8719 #默認8719,假如被佔用了會自動從8719開始依次+1掃描。直至找到未被佔用的端口
# 暴露監控
management:
endpoints:
web:
exposure:
include: '*'
- 主啓動類添加註解
@EnableDiscoveryClient
@SpringBootApplication
- 控制層針對服務限流的備用方法,至於限流方式,就直接再sentinel控制檯直接添加即可,@SentinelResource的value參數就是對應的資源名,裏面也可以加fallback調用斷路器備用響應方法,使用跟@HystrixCommand差不多。
@RestController
@SentinelResource(fallbackClass = "Global_FallbackMethod")
public class TestController {
@SentinelResource(value = "test",blockHandlerClass = CustomerBlockHandle.class,blockHandler ="deal_test")
@GetMapping("/test")
public String test(@RequestParam(value = "p") String p){
return "(●'◡'●)沒有被限流";
}
// public String test(String p,BlockException blockException){
// return "-------------deal list ┭┮﹏┭┮";
// }
//全局響應Fallback方法 服務降級觸發熔斷
public Object Global_FallbackMethod(){
return "┭┮﹏┭┮全局熔斷器方法觸發了,嗚嗚嗚!";
}
}
統一處理限流的備用方法類
public class CustomerBlockHandle {
public static String deal_test(String p, BlockException blockException){
return "┭┮﹏┭┮被限流了";
}
}
- 啓動後,再sentinel控制檯並沒有發現該服務,但是nacos卻已經發現該服務成功註冊了,是因爲需要發送一次請求,網頁訪問該服務,sentinel才能監控到(Sentinel採用的是懶加載)。
微服務消費者(使用feign方式)
- 導入pom
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-sentinel</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
- 配置yml
server:
port: 80
spring:
application:
name: nacos-test-consumer
cloud:
nacos:
discovery:
server-addr: localhost:8848
sentinel:
transport:
dashboard: localhost:8080
port: 8719
service-url:
nacos-user-service: http://cloudalibaba-sentinel-service
#對Feign的支持
feign:
sentinel:
enabled: true
- 配置接口以及接口的服務降級備用響應的實現類
@FeignClient(value = "cloudalibaba-sentinel-service",fallback = DeptFallbackService.class)
@Component
public interface TestService {
@GetMapping("/test")
public String test();
}
@Component // 服務降級
public class DeptFallbackService implements DeptService {
public String test() {
return "┭┮﹏┭┮服務被降級了,不能使用~";
}
}
- controller層服務調用
@Autowired
private TestService testService ;
@GetMapping(value = "/consumer/test")
public String test(){
return testService.test();
}
- 主啓動類
@EnableDiscoveryClient
@SpringBootApplication
@EnableFeignClients
public class test80
{
public static void main(String[] args) {
SpringApplication.run(test80.class, args);
}
}