分布式服務(wù)熔斷降級(jí)限流利器至Hystrix

全文概覽

[TOC]

為什么需要hystrix

hystrix官網(wǎng)地址github

  • Hystrix同樣是netfix公司在分布式系統(tǒng)中的貢獻(xiàn)。同樣的也進(jìn)入的不維護(hù)階段。不維護(hù)不代表被淘汰。只能說(shuō)明推陳出新技術(shù)在不斷迭代。曾今的輝煌曾經(jīng)的設(shè)計(jì)還是值得我們?nèi)W(xué)習(xí)的。

  • 在分布式環(huán)境中,服務(wù)調(diào)度是特色也是頭疼的一塊。在服務(wù)治理章節(jié)我們介紹了服務(wù)治理的功能。前一課我們也介紹了ribbon、feign進(jìn)行服務(wù)調(diào)用?,F(xiàn)在自然的到了服務(wù)監(jiān)控管理了。hystrix就是對(duì)服務(wù)進(jìn)行隔離保護(hù)。以實(shí)現(xiàn)服務(wù)不會(huì)出現(xiàn)連帶故障。導(dǎo)致整個(gè)系統(tǒng)不可用

image-20210414145650113
  • 如上圖所示,當(dāng)多個(gè)客戶端進(jìn)行服務(wù)調(diào)用Aservice時(shí),而在分布式系統(tǒng)中Aservice存在三臺(tái)服務(wù),其中Aservice某些邏輯需要Bservice處理。Bservice在分布式系統(tǒng)中部署了兩臺(tái)服務(wù)。這個(gè)時(shí)候因?yàn)榫W(wǎng)絡(luò)問(wèn)題導(dǎo)致Aservice中有一臺(tái)和Bservice的通信異常。如果Bservice是做日志處理的。在整個(gè)系統(tǒng)看來(lái)日志丟了和系統(tǒng)宕機(jī)比起來(lái)應(yīng)該無(wú)所謂了。但是這個(gè)時(shí)候因?yàn)榫W(wǎng)絡(luò)通信問(wèn)題導(dǎo)致Aservice整個(gè)服務(wù)不可用了。有點(diǎn)得不嘗試。
image-20210414153046542
  • 在看上圖 。 A-->B-->C-->D 。此時(shí)D服務(wù)宕機(jī)了。C因?yàn)镈宕機(jī)出現(xiàn)處理異常。但是C的線程卻還在為B響應(yīng)。這樣隨著并發(fā)請(qǐng)求進(jìn)來(lái)時(shí),C服務(wù)線程池出現(xiàn)爆滿導(dǎo)致CPU上漲。在這個(gè)時(shí)候C服務(wù)的其他業(yè)務(wù)也會(huì)受到CPU上漲的影響導(dǎo)致響應(yīng)變慢。

特色功能

Hystrix是一個(gè)低延遲和容錯(cuò)的第三方組件庫(kù)。旨在隔離遠(yuǎn)程系統(tǒng)、服務(wù)和第三方庫(kù)的訪問(wèn)點(diǎn)。官網(wǎng)上已經(jīng)停止維護(hù)并推薦使用resilience4j。但是國(guó)內(nèi)的話我們有springcloud alibaba。

Hystrix 通過(guò)隔離服務(wù)之間的訪問(wèn)來(lái)實(shí)現(xiàn)分布式系統(tǒng)中延遲及容錯(cuò)機(jī)制來(lái)解決服務(wù)雪崩場(chǎng)景并且基于hystrix可以提供備選方案(fallback)。

  • 對(duì)網(wǎng)絡(luò)延遲及故障進(jìn)行容錯(cuò)
  • 阻斷分布式系統(tǒng)雪崩
  • 快速失敗并平緩恢復(fù)
  • 服務(wù)降級(jí)
  • 實(shí)時(shí)監(jiān)控、警報(bào)

99.99^{30} = 99.7\% \quad uptime \\ 0.3\% \quad of \quad 1 \quad billion \quad requests \quad = \quad 3,000,000 \quad failures \\ 2+ \quad hours \quad downtime/month \quad even \quad if \quad all \quad dependencies \quad have \quad excellent \quad uptime.

  • 上面試官網(wǎng)給出的一個(gè)統(tǒng)計(jì)。在30臺(tái)服務(wù)中每臺(tái)出現(xiàn)異常的概覽是0.01%。一億個(gè)請(qǐng)求就會(huì)有300000失敗。這樣換算下每個(gè)月至少有2小時(shí)停機(jī)。這對(duì)于互聯(lián)網(wǎng)系統(tǒng)來(lái)說(shuō)是致命的。
image-20210414164135471
  • 上圖是官網(wǎng)給出的兩種情況。和我們上章節(jié)的類似。都是介紹服務(wù)雪崩的場(chǎng)景。

項(xiàng)目準(zhǔn)備

  • 在openfeign專題中我們就探討了基于feign實(shí)現(xiàn)的服務(wù)熔斷當(dāng)時(shí)說(shuō)了內(nèi)部就是基于hystrix。當(dāng)時(shí)我們也看了pom內(nèi)部的結(jié)構(gòu)在eureka中內(nèi)置ribbon的同時(shí)也內(nèi)置了hystrix模塊。
image-20210414165134167
  • 雖然包里面包含了hystrix 。我們還是引入對(duì)應(yīng)的start開啟相關(guān)配置吧。這里其實(shí)就是在openfeign專題中的列子。在那個(gè)專題我們提供了PaymentServiceFallbackImpl、PaymentServiceFallbackFactoryImpl兩個(gè)類作為備選方案。不過(guò)當(dāng)時(shí)我們只需指出openfeign支持設(shè)置兩種方式的備選方案。今天我們

    <!--hystrix-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
    </dependency>
    

    演示下傳統(tǒng)企業(yè)沒有備選方案的情況會(huì)發(fā)生什么災(zāi)難。

image-20210414171119461
image-20210414171529143

接口測(cè)試

  • 首先我們對(duì)payment#createByOrder接口進(jìn)行測(cè)試。查看下響應(yīng)情況

    image-20210415103254641
  • 在測(cè)試payment#getTimeout/id方法。

    image-20210415103254641
    • 現(xiàn)在我們用jemeter來(lái)壓測(cè)payment#getTimeOut/id這個(gè)接口。一位需要4S等待會(huì)照成資源消耗殆盡問(wèn)題。這個(gè)時(shí)候我們的payment#createByOrder也會(huì)被阻塞。
    image-20210415103254641
    • spring中默認(rèn)的tomcat的最大線程數(shù)是200.為了保護(hù)我們辛苦的筆記本。這里我們將線程數(shù)設(shè)置小點(diǎn)。這樣我們更容易復(fù)現(xiàn)線程被打滿的情況。線程滿了就會(huì)影響到payment#createByOrder接口。
    image-20210415103018785
  • 上面我們壓測(cè)的是payment的原生接口。如果壓測(cè)的是order模塊。如果沒有在openfeign中配置fallback。那么order服務(wù)就會(huì)因?yàn)閜ayment#getTimeOut/id接口并發(fā)導(dǎo)致線程滿了從而導(dǎo)致order模塊響應(yīng)緩慢。這就是雪崩效應(yīng)。下面我們從兩個(gè)方面來(lái)解決雪崩的發(fā)生。

業(yè)務(wù)隔離

  • 上面的場(chǎng)景發(fā)生是因?yàn)閜ayment#createByOrder 和payment#getTimeOut/id同屬于payment服務(wù)。一個(gè)payment服務(wù)實(shí)際上就是一個(gè)Tomcat服務(wù)。同一個(gè)tomcat服務(wù)是有一個(gè)線程池的。 每次請(qǐng)求落到該tomcat 服務(wù)里就會(huì)去線程池中申請(qǐng)線程。獲取到線程了才能由線程來(lái)處理請(qǐng)求的業(yè)務(wù)。就是因?yàn)閠omcat內(nèi)共享線程池。所以當(dāng)payment#getTimeOut/id并發(fā)上來(lái)后就會(huì)搶空線程池。導(dǎo)致別的借口甚至是毫不相關(guān)的接口都沒有資源可以申請(qǐng)。只能干巴巴的等待資源的釋放。

  • 這就好比上班高峰期乘坐電梯因?yàn)槟骋粋€(gè)公司集中上班導(dǎo)致一段時(shí)間電梯全部被使用了。這時(shí)候國(guó)家領(lǐng)導(dǎo)過(guò)來(lái)也沒辦法上電梯。

  • 我們也知道這種情況很好解決。每個(gè)園區(qū)都會(huì)有專用電梯供特殊使用。

  • 我們解決上述問(wèn)題也是同樣的思路。進(jìn)行隔離。不同的接口有不同的線程池。這樣就不會(huì)造成雪崩。

線程隔離

image-20210415112638886
  • 還記得我們上面為了演示并發(fā)將order模塊的最大線程數(shù)設(shè)置為10.這里我們通過(guò)測(cè)試工具調(diào)用下order/getpayment/1這個(gè)接口看看日志打印情況

    image-20210415142629374
  • 我們接口調(diào)用的地方將當(dāng)前線程打印出來(lái)。我們可以看到一只都是那10個(gè)線程在來(lái)回的使用。這也是上面為什么會(huì)造成雪崩現(xiàn)象。

    @HystrixCommand(
            groupKey = "order-service-getPaymentInfo",
            commandKey = "getPaymentInfo",
            threadPoolKey = "orderServicePaymentInfo",
            commandProperties = {
                    @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "1000")
            },
            threadPoolProperties = {
                    @HystrixProperty(name = "coreSize" ,value = "6"),
                    @HystrixProperty(name = "maxQueueSize",value = "100"),
                    @HystrixProperty(name = "keepAliveTimeMinutes",value = "2"),
                    @HystrixProperty(name = "queueSizeRejectionThreshold",value = "100")

            },
            fallbackMethod = "getPaymentInfoFallback"
    )
    @RequestMapping(value = "/getpayment/{id}",method = RequestMethod.GET)
    public ResultInfo getPaymentInfo(@PathVariable("id") Long id) {
        log.info(Thread.currentThread().getName());
        return restTemplate.getForObject(PAYMENT_URL+"/payment/get/"+id, ResultInfo.class);
    }
    public ResultInfo getPaymentInfoFallback(@PathVariable("id") Long id) {
        log.info("已經(jīng)進(jìn)入備選方案了,下面交由自由線程執(zhí)行"+Thread.currentThread().getName());
        return new ResultInfo();
    }
  @HystrixCommand(
            groupKey = "order-service-getpaymentTimeout",
            commandKey = "getpaymentTimeout",
            threadPoolKey = "orderServicegetpaymentTimeout",
            commandProperties = {
                    @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "10000")
            },
            threadPoolProperties = {
                    @HystrixProperty(name = "coreSize" ,value = "3"),
                    @HystrixProperty(name = "maxQueueSize",value = "100"),
                    @HystrixProperty(name = "keepAliveTimeMinutes",value = "2"),
                    @HystrixProperty(name = "queueSizeRejectionThreshold",value = "100")

            }
    )
    @RequestMapping(value = "/getpaymentTimeout/{id}",method = RequestMethod.GET)
    public ResultInfo getpaymentTimeout(@PathVariable("id") Long id) {
        log.info(Thread.currentThread().getName());
        return orderPaymentService.getTimeOut(id);
    }
  • 這里演示效果不好展示,我就直接展示數(shù)據(jù)吧。
并發(fā)量在getpaymentTimeout getpaymentTimeout/{id} /getpayment/{id}
20 三個(gè)線程打滿后一段時(shí)間開始報(bào)錯(cuò) 可以正常響應(yīng);也會(huì)慢,cpu線程切換需要時(shí)間
30 同上 同上
50 同上 也會(huì)超時(shí),因?yàn)閛rder調(diào)用payment服務(wù)壓力會(huì)受影響
  • 如果我們將hystrix加載payment原生服務(wù)就不會(huì)出現(xiàn)上面第三條情況。為什么我會(huì)放在order上就是想讓大家看看雪崩的場(chǎng)景。在并發(fā)50的時(shí)候因?yàn)閜ayment設(shè)置的最大線程也是10,他本身也是有吞吐量的。在order#getpyament/id接口雖然在order模塊因?yàn)閔ystrix線程隔離有自己的線程運(yùn)行,但是因?yàn)樵?wù)不給力導(dǎo)致自己調(diào)用超時(shí)從而影響運(yùn)行的效果。這樣演示也是為了后續(xù)引出fallback解決雪崩的一次場(chǎng)景模擬吧。
  • 我們可以在payment服務(wù)中通過(guò)hystrix設(shè)置fallback。保證payment服務(wù)低延遲從而保證order模塊不會(huì)因?yàn)閜ayment自己緩慢導(dǎo)致order#getpayment這種正常接口異常。
  • 還有一點(diǎn)雖然通過(guò)hystrix進(jìn)行線程隔離了。但是我們?cè)谶\(yùn)行其他接口時(shí)響應(yīng)時(shí)間也會(huì)稍長(zhǎng)點(diǎn)。因?yàn)镃PU在進(jìn)行線程切換的時(shí)候是有開銷的。這一點(diǎn)也是痛點(diǎn)。我們并不能隨心所欲的進(jìn)行線程隔離的。這就引出我們的信號(hào)量隔離了。

信號(hào)量隔離

  • 關(guān)于信號(hào)量隔離這里也就不演示了。演示的意義不是很大
   @HystrixCommand(
            commandProperties = {
                    @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "1000"),
                    @HystrixProperty(name = HystrixPropertiesManager.EXECUTION_ISOLATION_STRATEGY,value = "SEMAPHORE"),
                    @HystrixProperty(name = HystrixPropertiesManager.EXECUTION_ISOLATION_SEMAPHORE_MAX_CONCURRENT_REQUESTS,value = "6")
            },
            fallbackMethod = "getPaymentInfoFallback"
    )
  • 我們?nèi)缟吓渲帽硎拘盘?hào)量最大為6 。 表示并發(fā)6之后就會(huì)進(jìn)行等待。等待超時(shí)時(shí)間未1s。
措施 優(yōu)點(diǎn) 缺點(diǎn) 超時(shí) 熔斷 異步
線程隔離 一個(gè)調(diào)用一個(gè)線程池;互相不干擾;保證高可用 cpu線程切換開銷
信號(hào)量隔離 避免CPU切換。高效 在高并發(fā)場(chǎng)景下需要存儲(chǔ)信號(hào)量變大 × ×
  • 除了線程隔離、信號(hào)量隔離等隔離手段我們可以通過(guò)請(qǐng)求合并、接口數(shù)據(jù)緩存等手段加強(qiáng)穩(wěn)定性。

服務(wù)降級(jí)

觸發(fā)條件

  • 程序發(fā)生除HystrixBadRequestException異常。
  • 服務(wù)調(diào)用超時(shí)
  • 服務(wù)熔斷
  • 線程池、信號(hào)量不夠
image-20210415180423428
  • 在上面我們的timeout接口。不管是線程隔離還是信號(hào)量隔離在條件滿足的時(shí)候就會(huì)直接拒絕后續(xù)請(qǐng)求。這樣太粗暴了。上面我們也提到了fallback。

  • 還記的上面我們order50個(gè)并發(fā)的timeout的時(shí)候會(huì)導(dǎo)致getpayment接口異常,當(dāng)時(shí)定位了是因?yàn)樵鷓ayment服務(wù)壓力撐不住導(dǎo)致的。如果我們?cè)趐ayment上加入fallback就能保證在資源不足的時(shí)候也能快速響應(yīng)。這樣至少能保證order#getpayment方法的可用性。

    image-20210415180423428
    • 但是這種配置屬于實(shí)驗(yàn)性配置。在真實(shí)生產(chǎn)中我們不可能在每個(gè)方法上配置fallback的。這樣愚蠢至極。

    • hystrix除了在方法上特殊定制的fallback以外,還有一個(gè)全局的fallback。只需要在類上通過(guò)@DefaultProperties(defaultFallback = "globalFallback")來(lái)實(shí)現(xiàn)全局的備選方案。一個(gè)方法滿足觸發(fā)降級(jí)的條件時(shí)如果該請(qǐng)求對(duì)應(yīng)的HystrixCommand注解中沒有配置fallback則使用所在類的全局fallback。如果全局也沒有則拋出異常。

      不足

      • 雖然DefaultProperties 可以避免每個(gè)接口都配置fallback。但是這種的全局好像還不是全局的fallback。我們還是需要每個(gè)類上配置fallback。筆者查閱了資料好像也沒有
      • 但是在openfeign專題里我們說(shuō)了openfeign結(jié)合hystrix實(shí)現(xiàn)的服務(wù)降級(jí)功能。還記的里面提到了一個(gè)FallbackFactory這個(gè)類嗎。這個(gè)類可以理解成spring的BeanFactory。這個(gè)類是用來(lái)產(chǎn)生我們所需要的FallBack的。我們?cè)谶@個(gè)工廠里可以生成一個(gè)通用類型的fallback的代理對(duì)象。代理對(duì)象可以根據(jù)代理方法的方法簽名進(jìn)行入?yún)⒑统鰠ⅰ?/li>
      • 這樣我們可以在所有的openfeign地方配置這個(gè)工廠類。這樣的話就避免的生成很多個(gè)fallback。 美中不足的還是需要每個(gè)地方都指定一下。關(guān)于FallBackFactory感興趣的可以下載源碼查看或者進(jìn)主頁(yè)查看openfeign專題。

服務(wù)熔斷

  @HystrixCommand(
            commandProperties = {
                    @HystrixProperty(name = "circuitBreaker.enabled",value = "true"),  //是否開啟斷路器
                    @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value = "10"),   //請(qǐng)求次數(shù)
                    @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds",value = "10000"),  //時(shí)間范圍
                    @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage",value = "60"), //失敗率達(dá)到多少后跳閘
            },
            fallbackMethod = "getInfoFallback"
    )
    @RequestMapping(value = "/get", method = RequestMethod.GET)
    public ResultInfo get(@RequestParam Long id) {
        if (id < 0) {
            int i = 1 / 0;
        }
        log.info(Thread.currentThread().getName());
        return orderPaymentService.get(id);
    }
    public ResultInfo getInfoFallback(@RequestParam Long id) {

        return new ResultInfo();
    }
  • 首先我們通過(guò)circuitBreaker.enabled=true開啟熔斷器
  • circuitBreaker.requestVolumeThreshold設(shè)置統(tǒng)計(jì)請(qǐng)求次數(shù)
  • circuitBreaker.sleepWindowInMilliseconds 設(shè)置時(shí)間滑動(dòng)單位 , 在觸發(fā)熔斷后多久進(jìn)行嘗試開放,及俗稱的半開狀態(tài)
  • circuitBreaker.errorThresholdPercentage 設(shè)置觸發(fā)熔斷開關(guān)的臨界條件
  • 上面的配置如果最近的10次請(qǐng)求錯(cuò)誤率達(dá)到60% ,則觸發(fā)熔斷降級(jí) , 在10S內(nèi)都處于熔斷狀態(tài)服務(wù)進(jìn)行降級(jí)。10S后半開嘗試獲取服務(wù)最新狀態(tài)
  • 下面我們通過(guò)jmeter進(jìn)行接口http://localhost/order/get?id=-1進(jìn)行20次測(cè)試。雖然這20次無(wú)一例額外都會(huì)報(bào)錯(cuò)。但是我們會(huì)發(fā)現(xiàn)一開始報(bào)錯(cuò)是因?yàn)槲覀兇a里的錯(cuò)誤。后面的錯(cuò)誤就是hystrix熔斷的錯(cuò)誤了。一開始試by zero 錯(cuò)誤、后面就是short-circuited and fallback failed 熔斷錯(cuò)誤了
image
  • 正常我們?cè)趆ystrix中會(huì)配置fallback , 關(guān)于fallback兩種方式我們上面降級(jí)章節(jié)已經(jīng)實(shí)現(xiàn)了。這里是為了方便看到錯(cuò)誤的不同特意放開了。
image-20210421163842061
  • 在HystrixCommand中配置的參數(shù)基本都是在HystrixPropertiesManager對(duì)象中。我們可以看到關(guān)于熔斷器的配置有6個(gè)參數(shù)?;揪褪俏覀兩厦娴乃膫€(gè)配置

服務(wù)限流

  • 服務(wù)降級(jí)我們上面提到的兩種隔離就是實(shí)現(xiàn)限流的策略。

請(qǐng)求合并

  • 除了熔斷、降級(jí)、限流意外hystrix還為我們提供了請(qǐng)求合并。顧名思義就是將多個(gè)請(qǐng)求合并成一個(gè)請(qǐng)求已達(dá)到降低并發(fā)的問(wèn)題。
  • 比如說(shuō)我們order有個(gè)接個(gè)是查詢當(dāng)個(gè)訂單信息order/getId?id=1 突然有一萬(wàn)個(gè)請(qǐng)求過(guò)來(lái)。為了緩解壓力我們集中一下請(qǐng)求每100個(gè)請(qǐng)求調(diào)用一次order/getIds?ids=xxxxx 。這樣我們最終到payment模塊則是10000/100=100個(gè)請(qǐng)求。下面我們通過(guò)代碼配置實(shí)現(xiàn)下請(qǐng)求合并。

HystrixCollapser

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface HystrixCollapser {
    String collapserKey() default "";

    String batchMethod();

    Scope scope() default Scope.REQUEST;

    HystrixProperty[] collapserProperties() default {};
}
屬性 含義
collapserKey 唯一標(biāo)識(shí)
batchMethod 請(qǐng)求合并處理方法。即合并后需要調(diào)用的方法
scope 作用域;兩種方式[REQUEST, GLOBAL] ; <br />REQUEST : 在同一個(gè)用戶請(qǐng)求中達(dá)到條件將會(huì)合并<br />GLOBAL : 任何線程的請(qǐng)求都會(huì)加入到這個(gè)全局統(tǒng)計(jì)中
HystrixProperty[] 配置相關(guān)參數(shù)
image-20210422094851902
  • 在Hystrix中所有的properties配置都會(huì)在HystrixPropertiesManager.java中。我們?cè)诶锩婵梢哉业紺ollapser只有兩個(gè)相關(guān)的配置。分別表示最大請(qǐng)求數(shù)和統(tǒng)計(jì)時(shí)間單元。
    @HystrixCollapser(
            scope = com.netflix.hystrix.HystrixCollapser.Scope.GLOBAL,
            batchMethod = "getIds",
            collapserProperties = {
                    @HystrixProperty(name = HystrixPropertiesManager.MAX_REQUESTS_IN_BATCH , value = "3"),
                    @HystrixProperty(name = HystrixPropertiesManager.TIMER_DELAY_IN_MILLISECONDS, value = "10")
            }
    )
    @RequestMapping(value = "/getId", method = RequestMethod.GET)
    public ResultInfo getId(@RequestParam Long id) {
        if (id < 0) {
            int i = 1 / 0;
        }
        log.info(Thread.currentThread().getName());
        return null;
    }
    @HystrixCommand
    public List<ResultInfo> getIds(List<Long> ids) {
        System.out.println(ids.size()+"@@@@@@@@@");
        return orderPaymentService.getIds(ids);
    }
  • 上面我們配置了getId會(huì)走getIds請(qǐng)求,最多是10S三個(gè)請(qǐng)求會(huì)合并在一起。然后getIds有payment服務(wù)在分別去查詢最終返回多個(gè)ResultInfo。
image
  • 我們通過(guò)jemeter進(jìn)行g(shù)etId接口壓測(cè),日志中ids的長(zhǎng)度最大是3 。 驗(yàn)證了我們上面getId接口的配置。這樣就能保證在出現(xiàn)高并發(fā)的時(shí)候會(huì)進(jìn)行接口合并降低TPS。

  • 上面我們是通過(guò)請(qǐng)求方法注解進(jìn)行接口合并處理。實(shí)際上內(nèi)部hystrix是通過(guò)HystrixCommand

工作流程

image-20210421171613835
  • 官網(wǎng)給出的流程圖示,并配備流程說(shuō)明一共是9部。下面我們就翻譯下。

  • ①、創(chuàng)建HystrixCommand或者HystrixObservableCommand對(duì)象

    • HystrixCommand : 用在依賴單個(gè)服務(wù)上
    • HystrixObservableCommand : 用在依賴多個(gè)服務(wù)上
  • ②、命令執(zhí)行,hystrrixCommand 執(zhí)行execute、queue ; hystrixObservableCommand執(zhí)行observe、toObservable

方法 作用
execute 同步執(zhí)行;返回結(jié)果對(duì)象或者異常拋出
queue 異步執(zhí)行;返回Future對(duì)象
observe 返回Observable對(duì)象
toObservable 返回Observable對(duì)象
  • ③、查看緩存是否開啟及是否命中緩存,命中則返回緩存響應(yīng)
  • ④、是否熔斷, 如果已經(jīng)熔斷則fallback降級(jí);如果熔斷器是關(guān)閉的則放行
  • ⑤、線程池、信號(hào)量是否有資源供使用。如果沒有足夠資源則fallback 。 有則放行
  • ⑥、執(zhí)行run或者construct方法。這兩個(gè)是hystrix原生的方法,java實(shí)現(xiàn)hystrix會(huì)實(shí)現(xiàn)兩個(gè)方法的邏輯,springcloud已經(jīng)幫我們封裝了。這里就不看這兩個(gè)方法了。如果執(zhí)行錯(cuò)誤或者超時(shí)則fallback。在此期間會(huì)將日志采集到監(jiān)控中心。
  • ⑦、計(jì)算熔斷器數(shù)據(jù),判斷是否需要嘗試放行;這里統(tǒng)計(jì)的數(shù)據(jù)會(huì)在hystrix.stream的dashboard中查看到。方便我們定位接口健康狀態(tài)
  • ⑧、在流程圖中我們也能看到④、⑤、⑥都會(huì)指向fallback。 也是我們俗稱的服務(wù)降級(jí)??梢姺?wù)降級(jí)是hystrix熱門業(yè)務(wù)啊。
  • ⑨、返回響應(yīng)

HystrixDashboard

  • hystrix 除了服務(wù)熔斷、降級(jí)、限流以外,還有一個(gè)重要的特性是實(shí)時(shí)監(jiān)控。并形成報(bào)表統(tǒng)計(jì)接口請(qǐng)求信息。

  • 關(guān)于hystrix的安裝也很簡(jiǎn)單,只需要在項(xiàng)目中配置actutor和hystrix-dashboard兩個(gè)模塊就行了

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
  • 啟動(dòng)類上添加EnableHystrixDashboard 就引入了dashboard了。 我們不需要進(jìn)行任何開發(fā)。這個(gè)和eureka一樣主需要簡(jiǎn)單的引包就可以了。
image-20210422161743942
  • 就這樣dashboard搭建完成了。dashboard主要是用來(lái)監(jiān)控hystrix的請(qǐng)求處理的。所以我們還需要在hystrix請(qǐng)求出將端點(diǎn)暴露出來(lái)。

  • 在使用了hystrix命令的模塊加入如下配置即可,我就在order模塊加入

@Component
public class HystrixConfig {
    @Bean
    public ServletRegistrationBean getServlet(){
        HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet();
        ServletRegistrationBean registrationBean = new ServletRegistrationBean(streamServlet);
        registrationBean.setLoadOnStartup(1);
        //注意這里配置的/hystrix.stream  最終訪問(wèn)地址就是 localhost:port/hystrix.stream ; 如果在配置文件中配置在新版本中是需要
        //加上actuator  即 localhost:port/actuator
        registrationBean.addUrlMappings("/hystrix.stream");
        registrationBean.setName("HystrixMetricsStreamServlet");
        return registrationBean;
    }
}
  • 然后我們?cè)L問(wèn)order模塊localhost/hystrix.stream 就會(huì)出現(xiàn)ping的界面。表示我們order模塊安裝監(jiān)控成功。當(dāng)然order也需要actuator模塊
  • 下面我們通過(guò)jmeter來(lái)壓測(cè)我們的熔斷、降級(jí)、限流接口在通過(guò)dashboard來(lái)看看各個(gè)狀態(tài)吧。
image
  • 上面的動(dòng)畫看起來(lái)我們的服務(wù)還是很忙的。想想如果是電商當(dāng)你看著每個(gè)接口的折線圖像不像就是你的心跳。太高的你就擔(dān)心的。太低了就沒有成就高。下面我們看看dashboard的指標(biāo)詳情
image-20210422163536850
  • 我們看看我們服務(wù)運(yùn)行期間各個(gè)接口的現(xiàn)狀。
image-20210422161504688

聚合監(jiān)控

  • 上面我們通過(guò)新建的模塊hystrix-dashboard 來(lái)對(duì)我們的order模塊進(jìn)行監(jiān)控。但是實(shí)際應(yīng)用中我們不可能只在order中配置hystrix的。
  • 我們只是在上面為了演示所以在order配置的?,F(xiàn)在我們?cè)趐ayment中也對(duì)hystrix中配置。那么我們就需要在dashboard中來(lái)回切換order、payment的監(jiān)控?cái)?shù)據(jù)了。
  • 所以我們的聚合監(jiān)控就來(lái)了。在進(jìn)行聚合監(jiān)控之前我們先將payment也引入hystrix。注意上面我們是通過(guò)bean方式注入hystrix.stream 的 。 訪問(wèn)前綴不需要actuator

新建hystrix-turbine

pom

<!--新增hystrix dashboard-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-turbine</artifactId>
        </dependency>
  • 主要就是新增turbine坐標(biāo),其他的就是hystrix , dashboard等模塊,具體查看結(jié)尾處源碼

yml

spring:
  application:
    name: cloud-hystrix-turbine

eureka:
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://localhost:7001/eureka
  instance:
    prefer-ip-address: true

# 聚合監(jiān)控

turbine:
  app-config: cloud-order-service,cloud-payment-service
  cluster-name-expression: "'default'"
  # 該處配置和url一樣。如果/actuator/hystrix.stream 的則需要配置actuator
  instanceUrlSuffix: hystrix.stream

啟動(dòng)類

啟動(dòng)類上添加EnableTurbine注解

image-20210423093456668



源碼

上述源碼

image-20210414153359481
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容