基於 Hystrix 信號量機制實現資源隔離

基於 Hystrix 信號量機制實現資源隔離

Hystrix 裏面核心的一項功能,其實就是所謂的資源隔離,要解決的最最核心的問題,就是將多個依賴服務的調用分別隔離到各自的資源池內。避免說對某一個依賴服務的調用,由於依賴服務的接口調用的延遲或者失敗,致使服務全部的線程資源所有耗費在這個服務的接口調用上。一旦說某個服務的線程資源所有耗盡的話,就可能致使服務崩潰,甚至說這種故障會不斷蔓延。java

Hystrix 實現資源隔離,主要有兩種技術:web

  • 線程池
  • 信號量

默認狀況下,Hystrix 使用線程池模式。緩存

前面已經說過線程池技術了,這一小節就來講說信號量機制實現資源隔離,以及這兩種技術的區別與具體應用場景。tomcat

信號量機制

信號量的資源隔離只是起到一個開關的做用,好比,服務 A 的信號量大小爲 10,那麼就是說它同時只容許有 10 個 tomcat 線程來訪問服務 A,其它的請求都會被拒絕,從而達到資源隔離和限流保護的做用。網絡

線程池與信號量區別

線程池隔離技術,並非說去控制相似 tomcat 這種 web 容器的線程。更加嚴格的意義上來講,Hystrix 的線程池隔離技術,控制的是 tomcat 線程的執行。Hystrix 線程池滿後,會確保說,tomcat 的線程不會由於依賴服務的接口調用延遲或故障而被 hang 住,tomcat 其它的線程不會卡死,能夠快速返回,而後支撐其它的事情。app

線程池隔離技術,是用 Hystrix 本身的線程去執行調用;而信號量隔離技術,是直接讓 tomcat 線程去調用依賴服務。信號量隔離,只是一道關卡,信號量有多少,就容許多少個 tomcat 線程經過它,而後去執行。ide

適用場景性能

  • 線程池技術,適合絕大多數場景,好比說咱們對依賴服務的網絡請求的調用和訪問、須要對調用的 timeout 進行控制(捕捉 timeout 超時異常)。
  • 信號量技術,適合說你的訪問不是對外部依賴的訪問,而是對內部的一些比較複雜的業務邏輯的訪問,而且系統內部的代碼,其實不涉及任何的網絡請求,那麼只要作信號量的普通限流就能夠了,由於不須要去捕獲 timeout 相似的問題。

信號量簡單 Demo

業務背景裏,比較適合信號量的是什麼場景呢?this

好比說,咱們通常來講,緩存服務,可能會將一些量特別少、訪問又特別頻繁的數據,放在本身的純內存中。spa

舉個栗子。通常咱們在獲取到商品數據以後,都要去獲取商品是屬於哪一個地理位置、省、市、賣家等,可能在本身的純內存中,好比就一個 Map 去獲取。對於這種直接訪問本地內存的邏輯,比較適合用信號量作一下簡單的隔離。

優勢在於,不用本身管理線程池啦,不用 care timeout 超時啦,也不須要進行線程的上下文切換啦。信號量作隔離的話,性能相對來講會高一些。

假如這是本地緩存,咱們能夠經過 cityId,拿到 cityName。

public class LocationCache {
    private static Map<Long, String> cityMap = new HashMap<>();

    static {
        cityMap.put(1L, "北京");
    }

    /** * 經過cityId 獲取 cityName * * @param cityId 城市id * @return 城市名 */
    public static String getCityName(Long cityId) {
        return cityMap.get(cityId);
    }
}
複製代碼

寫一個 GetCityNameCommand,策略設置爲信號量。run() 方法中獲取本地緩存。咱們目的就是對獲取本地緩存的代碼進行資源隔離。

public class GetCityNameCommand extends HystrixCommand<String> {

    private Long cityId;

    public GetCityNameCommand(Long cityId) {
        // 設置信號量隔離策略
        super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("GetCityNameGroup"))
                .andCommandPropertiesDefaults(HystrixCommandProperties.Setter()
                        .withExecutionIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.SEMAPHORE)));

        this.cityId = cityId;
    }

    @Override
    protected String run() {
        // 須要進行信號量隔離的代碼
        return LocationCache.getCityName(cityId);
    }
}
複製代碼

在接口層,經過建立 GetCityNameCommand,傳入 cityId,執行 execute() 方法,那麼獲取本地 cityName 緩存的代碼將會進行信號量的資源隔離。

@RequestMapping("/getProductInfo")
@ResponseBody
public String getProductInfo(Long productId) {
    HystrixCommand<ProductInfo> getProductInfoCommand = new GetProductInfoCommand(productId);

    // 經過command執行,獲取最新商品數據
    ProductInfo productInfo = getProductInfoCommand.execute();

    Long cityId = productInfo.getCityId();

    GetCityNameCommand getCityNameCommand = new GetCityNameCommand(cityId);
    // 獲取本地內存(cityName)的代碼會被信號量進行資源隔離
    String cityName = getCityNameCommand.execute();

    productInfo.setCityName(cityName);

    System.out.println(productInfo);
    return "success";
}
複製代碼
相關文章
相關標籤/搜索