分布式系統面臨的問題
複雜分布式體系結構中的應用程式有數十個依賴關系,每個依賴關系在某些時候将不可避免地失敗。

上圖圖中的請求需要調用A,P, H,I四個服務,如果一切順利則沒有什麼問題,關鍵是如果I服務逾時或者無響應會出現什麼情況呢?
服務雪崩
多個微服務之間調用的時候,假設微服務A調用微服務B和微服務C,微服務B和微服務C又調用其它的微服務,這就是所謂的“扇出”。如果扇出的鍊路上某個微服務的調用響應時間過長或者不可用,對微服務A的調用就會占用越來越多的系統資源,進而引起系統崩潰,所謂的“雪崩效應”。
對于高流量的應用來說,單一的後端依賴可能會導緻所有伺服器上的所有資源都在幾秒鐘内飽和。比失敗更糟糕的是,這些應用程式還可能導緻服務之間的延遲增加,備份隊列,線程和其他系統資源緊張,導緻整個系統發生更多的級聯故障。這些都表示需要對故障和延遲進行隔離和管理,以便單個依賴關系的失敗,不能取消整個應用程式或系統。
Hystrix是什麼?
Hystrix是一個用于處理分布式系統的延遲和容錯的開源庫,在分布式系統裡,許多依賴不可避免的會調用失敗,比如逾時、異常等,Hystrix能夠保證在一個依賴出問題的情況下,不會導緻整體服務失敗,避免級聯故障,以提高分布式系統的彈性。
“斷路器”本身是一種開關裝置,當某個服務單元發生故障之後,通過斷路器的故障監控(類似熔斷保險絲),向調用方傳回一個符合預期的、可處理的備選響應(FallBack),而不是長時間的等待或者抛出調用方無法處理的異常,這樣就保證了服務調用方的線程不會被長時間、不必要地占用,進而避免了故障在分布式系統中的蔓延,乃至雪崩。
Hystrix的作用
- 服務熔斷
- 服務降級
- 服務限流
- 服務監控
- 等等
熔斷機制是應對雪崩效應的一種微服務鍊路保護機制。
當扇對外連結路的某個微服務不可用或者響應時間太長時,會進行服務的降級,進而熔斷該節點微服務的調用,快速傳回"錯誤"的響應資訊。當檢測到該節點微服務調用響應正常後恢複調用鍊路。在SpringCloud架構裡熔斷機制通過Hystrix實作。Hystrix會監控微服務間調用的狀況,當失敗的調用到一定門檻值,預設是5秒内20次調用失敗就會啟動熔斷機制。熔斷機制的注解是@HystrixCommand。
使用hystrix所需要的依賴
<!-- hystrix -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
引入完準備好的依賴之後,在EurekaClient用戶端中有一個提供服務的Controller,其中就一個根據id擷取對象的方法。當我們通路這個服務方法如果傳入進來的id在資料庫中不存在,就會傳回一個空對象,
在這裡當傳回對象為空的時候我們手動抛出一個異常資訊,當伺服器遇到異常資訊之後會去尋找fallbackMethod中的回調方法
package com.yjc.controller;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.yjc.entity.Dept;
import com.yjc.service.DeptService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
public class DeptController
{
@Autowired
private DeptService service;
@RequestMapping(value="/dept/get/{id}",method=RequestMethod.GET)
@HystrixCommand(fallbackMethod = "processHystrix_Get")//發生異常之後的回調方法
public Dept get(@PathVariable("id") Long id)
{
Dept dept = service.get(id);
//當沒有此條記錄的時候,手動抛出異常
if(null == dept)
{
throw new RuntimeException("該ID:"+id+"沒有沒有對應的資訊");
}
return dept;
}
public Dept processHystrix_Get(@PathVariable("id") Long id)
{
Dept dept=new Dept();
dept.setDeptno(id);
dept.setDname("該ID:"+id+"沒有沒有對應的資訊,null--@HystrixCommand");
dept.setDb_source("no this database in MySQL");
return dept;
}
}
在主啟動類中添加對hystrixR熔斷機制的支援
package com.yjc;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@EnableEurekaClient
@SpringBootApplication
@EnableCircuitBreaker//開啟hystrixR熔斷機制的支援
public class Hystrix8001 {
public static void main(String[] args) {
SpringApplication.run(Hystrix8001.class, args);
}
}
開始對服務進行消費
第一種情況資料庫中有id為1的記錄,一且正常
第二種情況資料庫中id值不存在的記錄,發生異常,進行預先設定好的回調函數,傳回一個自定義的對象,這樣不會影響整體的運作情況
服務降級
當伺服器壓力劇增的情況下,根據實際業務情況及流量,對一些服務和頁面有政策的不處理或換種簡單的方式處理,進而釋放伺服器資源以保證核心交易正常運作或高效運作。
整體資源快不夠了,忍痛将某些服務先關掉,待渡過難關,再開啟回來。服務降級處理是在用戶端實作完成的,與服務端沒有關系
在所有EurekaClient共有的datajpa中(包含實體類和Service接口),添加一個DeptClientService接口
package com.yjc.service;
import com.yjc.entity.Dept;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import java.util.List;
//value是所有服務的名稱,更據名稱通路服務,實作負載均衡,fallbackFactory當接口中的方法出現異常的時候,走的回調方法是哪個類中的方法
@FeignClient(value = "MYCLOUD",fallbackFactory = DeptClientServiceFallbackFactory.class) public interface DeptClientService {
public interface DeptClientService
{ //服務方法
@RequestMapping(value = "/dept/get/{id}",method = RequestMethod.GET)
public Dept get(@PathVariable("id") long id);
}
建立一個FallbackFactory實作FallbackFactory<T>接口,泛型類類型就是需要做服務降級的那個接口,也就是上面定義DeptClientService,然後重寫泛型接口中的方法
package com.yjc.service;
import com.yjc.entity.Dept;
import feign.hystrix.FallbackFactory;
import org.springframework.stereotype.Component;
import java.util.List;
@Component //必須要有的
public class DeptClientServiceFallbackFactory implements FallbackFactory<DeptClientService> {
@Override
public DeptClientService create(Throwable throwable)
{
return new DeptClientService() {
@Override
public Dept get(long id)
{
Dept dept=new Dept();
dept.setDeptno(id);
dept.setDname("該ID:"+id+"沒有沒有對應的資訊,Consumer用戶端提供的降級資訊,此刻服務Provider已經關閉");
dept.setDb_source("no this database in MySQL");
return dept;
}
};
}
}
接着修改具有Feign功能的消費者application.yml配置檔案,填寫如下配置
feign:
hystrix:
enabled: true
啟動EurekaServer,EurekaClient進行測試,
故意關閉EurekaClient再進行測試