Hystrix線程隔離技術解析-線程池

認識Hystrix

Hystrix是Netflix開源的一款容錯框架,包含常用的容錯方法:線程隔離、信號量隔離、降級策略、熔斷技術。
在高并發訪問下,系統所依賴的服務的穩定性對系統的影響非常大,依賴有很多不可控的因素,比如網絡連接變慢,資源突然繁忙,暫時不可用,服務脫機等。我們要構建穩定、可靠的分布式系統,就必須要有這樣一套容錯方法。
本文主要討論線程隔離技術。

為什么要做線程隔離

比如我們現在有3個業務調用分別是查詢訂單、查詢商品、查詢用戶,且這三個業務請求都是依賴第三方服務-訂單服務、商品服務、用戶服務。三個服務均是通過RPC調用。當查詢訂單服務,假如線程阻塞了,這個時候后續有大量的查詢訂單請求過來,那么容器中的線程數量則會持續增加直致CPU資源耗盡到100%,整個服務對外不可用,集群環境下就是雪崩。如下圖


訂單服務不可用.png

整個tomcat容器不可用.png
Hystrix是如何通過線程池實現線程隔離的

Hystrix通過命令模式,將每個類型的業務請求封裝成對應的命令請求,比如查詢訂單->訂單Command,查詢商品->商品Command,查詢用戶->用戶Command。每個類型的Command對應一個線程池。創建好的線程池是被放入到ConcurrentHashMap中,比如查詢訂單:

final static ConcurrentHashMap<String, HystrixThreadPool> threadPools = new ConcurrentHashMap<String, HystrixThreadPool>();
threadPools.put(“hystrix-order”, new HystrixThreadPoolDefault(threadPoolKey, propertiesBuilder));

當第二次查詢訂單請求過來的時候,則可以直接從Map中獲取該線程池。具體流程如下圖:

hystrix線程執行過程和異步化.png

創建線程池中的線程的方法,查看源代碼如下:

public ThreadPoolExecutor getThreadPool(final HystrixThreadPoolKey threadPoolKey, HystrixProperty<Integer> corePoolSize, HystrixProperty<Integer> maximumPoolSize, HystrixProperty<Integer> keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
    ThreadFactory threadFactory = null;
    if (!PlatformSpecific.isAppEngineStandardEnvironment()) {
        threadFactory = new ThreadFactory() {
            protected final AtomicInteger threadNumber = new AtomicInteger(0);

            @Override
            public Thread newThread(Runnable r) {
                Thread thread = new Thread(r, "hystrix-" + threadPoolKey.name() + "-" + threadNumber.incrementAndGet());
                thread.setDaemon(true);
                return thread;
            }

        };
    } else {
        threadFactory = PlatformSpecific.getAppEngineThreadFactory();
    }

    final int dynamicCoreSize = corePoolSize.get();
    final int dynamicMaximumSize = maximumPoolSize.get();

    if (dynamicCoreSize > dynamicMaximumSize) {
        logger.error("Hystrix ThreadPool configuration at startup for : " + threadPoolKey.name() + " is trying to set coreSize = " +
                dynamicCoreSize + " and maximumSize = " + dynamicMaximumSize + ".  Maximum size will be set to " +
                dynamicCoreSize + ", the coreSize value, since it must be equal to or greater than the coreSize value");
        return new ThreadPoolExecutor(dynamicCoreSize, dynamicCoreSize, keepAliveTime.get(), unit, workQueue, threadFactory);
    } else {
        return new ThreadPoolExecutor(dynamicCoreSize, dynamicMaximumSize, keepAliveTime.get(), unit, workQueue, threadFactory);
    }
}

執行Command的方式一共四種,直接看官方文檔(https://github.com/Netflix/Hystrix/wiki/How-it-Works),具體區別如下:

  • execute():以同步堵塞方式執行run()。調用execute()后,hystrix先創建一個新線程運行run(),接著調用程序要在execute()調用處一直堵塞著,直到run()運行完成。

  • queue():以異步非堵塞方式執行run()。調用queue()就直接返回一個Future對象,同時hystrix創建一個新線程運行run(),調用程序通過Future.get()拿到run()的返回結果,而Future.get()是堵塞執行的。

  • observe():事件注冊前執行run()/construct()。第一步是事件注冊前,先調用observe()自動觸發執行run()/construct()(如果繼承的是HystrixCommand,hystrix將創建新線程非堵塞執行run();如果繼承的是HystrixObservableCommand,將以調用程序線程堵塞執行construct()),第二步是從observe()返回后調用程序調用subscribe()完成事件注冊,如果run()/construct()執行成功則觸發onNext()和onCompleted(),如果執行異常則觸發onError()。

  • toObservable():事件注冊后執行run()/construct()。第一步是事件注冊前,調用toObservable()就直接返回一個Observable<String>對象,第二步調用subscribe()完成事件注冊后自動觸發執行run()/construct()(如果繼承的是HystrixCommand,hystrix將創建新線程非堵塞執行run(),調用程序不必等待run();如果繼承的是HystrixObservableCommand,將以調用程序線程堵塞執行construct(),調用程序等待construct()執行完才能繼續往下走),如果run()/construct()執行成功則觸發onNext()和onCompleted(),如果執行異常則觸發onError()
    注:
    execute()和queue()是在HystrixCommand中,observe()和toObservable()是在HystrixObservableCommand 中。從底層實現來講,HystrixCommand其實也是利用Observable實現的(看Hystrix源碼,可以發現里面大量使用了RxJava),盡管它只返回單個結果。HystrixCommand的queue方法實際上是調用了toObservable().toBlocking().toFuture(),而execute方法實際上是調用了queue().get()。

如何應用到實際代碼中
package myHystrix.threadpool;

import com.netflix.hystrix.*;
import org.junit.Test;

import java.util.List;
import java.util.concurrent.Future;

/**
 * Created by wangxindong on 2017/8/4.
 */
public class GetOrderCommand extends HystrixCommand<List> {

    OrderService orderService;

    public GetOrderCommand(String name){
        super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("ThreadPoolTestGroup"))
                .andCommandKey(HystrixCommandKey.Factory.asKey("testCommandKey"))
                .andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey(name))
                .andCommandPropertiesDefaults(
                        HystrixCommandProperties.Setter()
                                .withExecutionTimeoutInMilliseconds(5000)
                )
                .andThreadPoolPropertiesDefaults(
                        HystrixThreadPoolProperties.Setter()
                                .withMaxQueueSize(10)   //配置隊列大小
                                .withCoreSize(2)    // 配置線程池里的線程數
                )
        );
    }

    @Override
    protected List run() throws Exception {
        return orderService.getOrderList();
    }

    public static class UnitTest {
        @Test
        public void testGetOrder(){
//            new GetOrderCommand("hystrix-order").execute();
            Future<List> future =new GetOrderCommand("hystrix-order").queue();
        }

    }
}
總結

執行依賴代碼的線程與請求線程(比如Tomcat線程)分離,請求線程可以自由控制離開的時間,這也是我們通常說的異步編程,Hystrix是結合RxJava來實現的異步編程。通過設置線程池大小來控制并發訪問量,當線程飽和的時候可以拒絕服務,防止依賴問題擴散。

線程隔離.png

線程隔離的優點:
[1]:應用程序會被完全保護起來,即使依賴的一個服務的線程池滿了,也不會影響到應用程序的其他部分。
[2]:我們給應用程序引入一個新的風險較低的客戶端lib的時候,如果發生問題,也是在本lib中,并不會影響到其他內容,因此我們可以大膽的引入新lib庫。
[3]:當依賴的一個失敗的服務恢復正常時,應用程序會立即恢復正常的性能。
[4]:如果我們的應用程序一些參數配置錯誤了,線程池的運行狀況將會很快顯示出來,比如延遲、超時、拒絕等。同時可以通過動態屬性實時執行來處理糾正錯誤的參數配置。
[5]:如果服務的性能有變化,從而需要調整,比如增加或者減少超時時間,更改重試次數,就可以通過線程池指標動態屬性修改,而且不會影響到其他調用請求。
[6]:除了隔離優勢外,hystrix擁有專門的線程池可提供內置的并發功能,使得可以在同步調用之上構建異步的外觀模式,這樣就可以很方便的做異步編程(Hystrix引入了Rxjava異步框架)。

盡管線程池提供了線程隔離,我們的客戶端底層代碼也必須要有超時設置,不能無限制的阻塞以致線程池一直飽和。

線程隔離的缺點:
[1]:線程池的主要缺點就是它增加了計算的開銷,每個業務請求(被包裝成命令)在執行的時候,會涉及到請求排隊,調度和上下文切換。不過Netflix公司內部認為線程隔離開銷足夠小,不會產生重大的成本或性能的影響。

The Netflix API processes 10+ billion Hystrix Command executions per day using thread isolation. Each API instance has 40+ thread-pools with 5–20 threads in each (most are set to 10).
Netflix API每天使用線程隔離處理10億次Hystrix Command執行。 每個API實例都有40多個線程池,每個線程池中有5-20個線程(大多數設置為10個)。

對于不依賴網絡訪問的服務,比如只依賴內存緩存這種情況下,就不適合用線程池隔離技術,而是采用信號量隔離,后面文章會介紹。

因此我們可以放心使用Hystrix的線程隔離技術,來防止雪崩這種可怕的致命性線上故障。

轉載請注明出處,并附上鏈接 http://www.lxweimin.com/p/df1525d58c20

參考資料:
https://github.com/Netflix/Hystrix/wiki

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,797評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,179評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,628評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,642評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,444評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,948評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,040評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,185評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,717評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,602評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,794評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,316評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,045評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,418評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,671評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,414評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,750評論 2 370

推薦閱讀更多精彩內容

  • 一、認識Hystrix Hystrix是Netflix開源的一款容錯框架,包含常用的容錯方法:線程池隔離、信號量隔...
    新棟BOOK閱讀 4,065評論 0 19
  • 一、認識Hystrix Hystrix是Netflix開源的一款容錯框架,包含常用的容錯方法:線程池隔離、信號量隔...
    新棟BOOK閱讀 26,498評論 1 37
  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,778評論 18 139
  • 導語:網上資料(尤其中文文檔)對hystrix基礎功能的解釋比較籠統,看了往往一頭霧水。為此,本文將通過若干dem...
    star24閱讀 99,861評論 36 119
  • (git上的源碼:https://gitee.com/rain7564/spring_microservices_...
    sprainkle閱讀 9,379評論 13 33