Hystrix入門demo


Hystrix入門demo

Hystrix的三種降級策略

  • 熔斷觸發降級
  • 超時觸發降級
  • 資源隔離觸發降級

熔斷觸發降級

​ 熔斷機制是一種保護性的機制,當系統某個服務失敗率過高的時候,將開啟熔斷器,對該服務的后續調用直接拒絕,進行fallback操作。

熔斷器開啟的兩個條件

  • 請求數達到設定的閾值
  • 請求的失敗數 / 總請求書 > 錯誤占比閾值

代碼展示

     

	/**
     * HystrixProperty的參數可參考 hystrixCommandProperties
     * 熔斷觸發降級
     * @return
     */
    @GetMapping("/circuitBreaker/{num}")
    @HystrixCommand(commandProperties  = {
            @HystrixProperty (name = "circuitBreaker.enabled" ,value = "true"),
            @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value ="5"),
            @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds" , value ="5000"),  //熔斷時間5秒
            @HystrixProperty(name="circuitBreaker.errorThresholdPercentage",value = "50")  //錯誤流程比例

    } ,fallbackMethod = "fallback")
    public String circuitBreaker(@PathVariable("num")int num){
        if(num%2==0){
            return "正常訪問";
        }

        throw new RuntimeException("");
    }

    public String fallback(int num){
        return "熔斷觸發降級";
    }

而且需要開啟熔斷器的開關

@SpringBootApplication
@EnableCircuitBreaker
public class App {
    public static void main(String[] args) {
        SpringApplication.run(App.class,args);
    }

}

參數解析:

@HystrixProperty (name = "circuitBreaker.enabled" ,value = "true") 開啟熔斷降級功能

@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value ="5") 最小請求次數,這里是5個請求

@HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds" , value ="5000") 熔斷時間5秒

@HystrixProperty(name="circuitBreaker.errorThresholdPercentage",value = "50") 錯誤比例超過50%,就熔斷


fallbackMethod = "fallback" 觸發回調的方法 , 當前類的下的方法名

注意: 請求服務方法和回調方法,入參要一致,不然會報錯的

功能解析:

  • 當請求錯誤的時候,會觸發fallback這個回調方法
  • 10s鍾之內,當發起了超過5次請求,且失敗率超過50%,熔斷自動開啟,從熔斷開啟到后續5s之內的請求,都不會進入到方法里,並且直接觸發fallback這個回調方法返回。

結果展示:

Hystrix入門demo-1

​ 分析: 可以看到我發送了三次請求:http://127.0.0.1:9091/hystrix/circuitBreaker/1 , 然后發送http://127.0.0.1:9091/hystrix/circuitBreaker/2 請求兩次,這兩次返回都是正常訪問的。發送第三次的時候就返回熔斷觸發降級了。 這是因為:前面發送了5個請求,3個失敗了,2個成功,但是我們設置了是5次請求且失敗率是50%以上觸發的。所以到第6個請求的時候觸發了熔斷降級,需要等待5秒后才能正常恢復正常。

超時觸發降級

​ 超時觸發降級:當系統某個服務請求需要時間超過這個服務正常的請求時間,可以認為該服務已經請求失敗,直接降級返回。

​ 項目經歷的場景:比如測試一個數據源是否有效,正常情況下的話(不管成功還是失敗),會很快就能返回了,有些情況,服務會一直請求訪問數據源而卡着。這個時候用超時觸發降級就能達到要求了。因為他卡着其實這個數據源你是不成功的,那我直接返回給前台告訴他數據源不可用就行了

代碼展示:

    /**
     * 超時時間觸發降級
     * @return
     */
    @GetMapping("/timeOut")
    @HystrixCommand(commandProperties  = {
        @HystrixProperty(name = "execution.timeout.enabled" , value = "true"),
        @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds" , value = "1000"),
    } ,fallbackMethod = "fallback")
    public String timeOut() throws InterruptedException {
        Thread.sleep(3000) ;
        return "正常訪問";

    }


    public String fallback(){
        return "觸發降級";
    }

參數解析:

@HystrixProperty(name = "execution.timeout.enabled" , value = "true"), 啟動超時觸發降級
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds" , value = "1000"), 超過1s就觸發降級

功能解析:

  • 當請求超過1s,就會觸發降級

資源隔離觸發降級

資源隔離觸發降級又分成兩種:

  • 信號量隔離
  • 線程池隔離

信號量隔離

​ 信號隔離是通過限制依賴服務的並發請求數,來控制隔離開關,它不會使用Hystrix管理的線程池處理請求。使用容器(Tomcat)的線程處理請求邏輯。特點:不涉及線程切換,資源調度,上下文的轉換等,相對效率高。觸發條件:信號量隔離也會啟動熔斷機制。如果請求並發數超標,則觸發熔斷,返回fallback數據。

     public String fallback(){
        return "觸發降級";
    }

	@GetMapping("/semaphore")
    @HystrixCommand(
            commandProperties = {
                    @HystrixProperty(name = "execution.isolation.strategy" , value = "SEMAPHORE"),
                    @HystrixProperty(name = "execution.isolation.semaphore.maxConcurrentRequests" , value = "2") //信號量大小
            },
            fallbackMethod = "fallback"
    )
    public String semaphore() throws InterruptedException {
        return "semaphore正常訪問";
    }

參數配置:

  • @HystrixProperty(name = "execution.isolation.strategy" , value = "SEMAPHORE"), 隔離的種類,可選值只有THREAD(線程池隔離)和SEMAPHORE(信號量隔離)。默認是THREAD線程池隔離。設置信號量隔離后,線程池相關配置失效。
  • @HystrixProperty(name = "execution.isolation.semaphore.maxConcurrentRequests" , value = "2") 信號量大小,這服務只能兩個請求同時訪問 信號量最大並發數。默認值是10。常見配置500~1000。

功能解析:

​ 這個方法不能超過2個線程以上同時訪問。

線程池隔離

​ hystrix進行資源隔離,其實是提供了一個抽象,叫做command,就是說,你如果要把對某一個依賴服務的所有調用請求,全部隔離在同一份資源池內對這個依賴服務的所有調用請求,全部走這個資源池內的資源,不會去用其他的資源了,這個就叫做資源隔離

​ 比如:對某一個商品服務所有的調用請求,全部隔離到一個線程池內,對商品服務的每次調用請求都封裝在一個command里面每個command(每次服務調用請求)都是使用線程池內的一個線程去執行的,所以哪怕現在這個商品服務同時發起的調用量已經到了1000了,但是線程池內就10個線程,其他的請求都會存到等待隊列中,最多就只會用這10個線程去執行請求,對商品服務的請求,因為接口調用延遲。不會將tomcat內部所有的線程資源全部耗盡。

使用線程隔離的好處:

  • 應用程序可以不受失控的第三方客戶端的威脅,如果第三方客戶端出現問題,可以通過降級來隔離依賴。
  • 當失敗的客戶端服務恢復時,線程池將會被清除,應用程序也會恢復,而不至於使整個Tomcat容器出現故障。
  • 簡而言之,由線程供的隔離功能可以使客戶端和應用程序優雅的處理各種變化,而不會造成中斷。

線程池的缺點

  • 線程最主要的缺點就是增加了CPU的計算開銷,每個command都會在單獨的線程上執行,這樣的執行方式會涉及到命令的排隊、調度和上下文切換。
  • Netflix在設計這個系統時,決定接受這個開銷的代價,來換取它所提供的好處,並且認為這個開銷是足夠小的,不會有重大的成本或者是性能影響。

代碼展示:

 private int num1 = 1;
   

@HystrixCommand(
            commandProperties = {
                    @HystrixProperty(name = "execution.isolation.strategy" , value = "THREAD"),  //使用線程池的隔離
                    @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds" , value = "3000"),  //超時設置為3秒

            },
            threadPoolProperties = {

                    @HystrixProperty(name = "coreSize" , value = "20"), //線程池大小
                    @HystrixProperty(name = "maxQueueSize" , value = "1000"), //等待隊列長度
                    @HystrixProperty(name = "keepAliveTimeMinutes", value = "2"),//線程存活時間
                    @HystrixProperty(name = "queueSizeRejectionThreshold" , value = "800"),  
            },
            groupKey = "ThreadService", commandKey = "thread" ,threadPoolKey = "ThreadService",
            fallbackMethod = "fallback"
            )
    public void  thread() throws  Exception  {
        Thread.sleep(1000);
        System.out.println(Thread.currentThread() + "正常訪問" + num1++);
    }



    public void fallback(){
        System.out.println("熔斷時間:" + new Date());
    }

參數解析:

  • ​ @HystrixProperty(name = "execution.isolation.strategy" , value = "THREAD") 使用線程池的隔離

  • ​ @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds" , value = "3000") 超時設置為3秒,默認是1s


  • ​ @HystrixProperty(name = "coreSize" , value = "20") 線程池大小,默認是5個

  • ​ @HystrixProperty(name = "maxQueueSize" , value = "1000") 等待隊列長度,注意 :該設置只會在初始化時有效,之后不能修改threadpool的queue size

  • ​ @HystrixProperty(name = "keepAliveTimeMinutes", value = "2") 線程存活時間,如果corePoolSize和maxPoolSize設成一樣(默認實現)該設置無效

  • ​ @HystrixProperty(name = "queueSizeRejectionThreshold" , value = "800"), 即使maxQueueSize沒有達到,達到queueSizeRejectionThreshold該值后,請求也會被拒絕。因為maxQueueSize不能被動態修改,這個參數將允許我們動態設置該值。if maxQueueSize == -1,該字段將不起作用


  • threadPoolKey 線程池的名字。 默認的threadpool key就是command group名稱

  • groupKey 群組的key

  • commandKey 代表了一類command,一般來說,代表了底層的依賴服務的一個接口

​ command group一般來說,可以是對應一個服務,多個command key對應這個服務的多個接口,多個接口的調用共享同一個線程池

如果說你的command key,要用自己的線程池,可以定義自己的threadpool key.

測試用例:

@Autowired
private ThreadService threadService;

@Test
public void  threadTest() throws Exception {


    System.out.println("開始時間: " + new Date());
    for (int i = 0; i < 100; i++) {
        new Thread(new Runnable() {
            public void run()  {
                try {
                    threadService.thread();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();
  }
    Thread.sleep(100000 );
    System.out.println("完成了");
}

結果測試展示:

image-20200915222146171

  • 可以留意到了線程的名字是ThreadService就是我們設置的threadPoolKey,看序號剛好就到20,跟我們想象的結果一樣的,證明了maxQueueSize是對的

  • 超時的熔斷時間-開始時間 = 4 秒 .這個是正常的。因為這里的開始時間是第一個啟動線程的開始時間,而不是那個熔斷的線程啟動的開始時間,算下來有4秒跟三秒差不多。其實也驗證了我們這個execution.isolation.thread.timeoutInMilliseconds的設置是正確的。

hystrix和openfeign的結合使用

這個可以直接在配置文件里application.yml中配置

feign:
  hystrix:
    enabled: true   #開始openFeigin的配置


hystrix:
  command:
    default:  #全局配置, feignclient#method(param)
      execution:
        timeout:
          enable: true       #開啟超時降級
        isolation:
          thread:
            timeoutInMilliseconds: 1000     #降級時間是1s
    SpringHystrixController#query():       #給SpringHystrixController類下的query方法定義降級策略
      execution:
        isolation:
          strategy: SEMAPHORE                #信號量降級
          semaphore:
            maxConcurrentRequests: 10         #信號量數量是10
    SpringHystrixController#insert():
      execution:
        isolation:
          strategy: THREAD
  threadpool:
    order-service:
      coreSize: 2
      maxQueueSize: 1000
      queueSizeRejectionThreshold:  800

注意:這些設置只針對於加了feignClient的方法

參數可以參考HystrixCommandPropertiesHystrixThreadPoolProperties進行設置

HystrixCommand的使用

​ 這個功能通過繼承HystrixCommand這個類來實現熔斷,因為這個是在代碼層面上的,其實可以動態的修改熔斷的規則。

使用:

 * 熔斷器設置
 * @author gyc
 * @date 2020/9/20
 */
public class CircuitBreakerService extends HystrixCommand<String> {


    private int num;

    static HystrixCommand.Setter CIRCUITBREAKER = HystrixCommand.Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("HystrixService"))
            .andCommandPropertiesDefaults(HystrixCommandProperties.Setter()
                    .withCircuitBreakerEnabled(true)
                    .withCircuitBreakerRequestVolumeThreshold(5)
                    .withCircuitBreakerErrorThresholdPercentage(50)
                    .withCircuitBreakerSleepWindowInMilliseconds(5000)
            );

    /**
     *
     * @param num
     */
    public CircuitBreakerService(int num) {
        super(CIRCUITBREAKER);
        this.num = num;
    }

    protected String run() throws Exception {
        if(num % 2 == 0){
            return "正常訪問";
        }

        throw new RuntimeException("");
    }

    @Override
    protected String getFallback() {
        return "熔斷降級";
    }
}

調用:

    @GetMapping("/circuitBreaker/{num}")
    public String circuitBreaker(@PathVariable("num")int num){
        return new CircuitBreakerService(num).execute();
    }

​ 這個熔斷器的功能其實跟上面@HystrixCommand熔斷器的功能是一樣的。其實我們可以發現,我們把要執行的代碼都寫到HystrixCommand.run()方法中,然后調用CircuitBreakerService.execute()就可以了。其實這個跟Thread線程使用的方法其實是一樣的。Thread也是把要執行的代碼寫到run()方法中,然后通過Thread.start()來做一個執行。

下面附上超時,線程池隔離,信號量隔離的代碼:

 //熔斷降級
    static HystrixCommand.Setter CIRCUITBREAKER = HystrixCommand.Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("HystrixService"))
            .andCommandPropertiesDefaults(HystrixCommandProperties.Setter()
                    .withCircuitBreakerEnabled(true)
                    .withCircuitBreakerRequestVolumeThreshold(5)
                    .withCircuitBreakerErrorThresholdPercentage(50)
                    .withCircuitBreakerSleepWindowInMilliseconds(5000)
            );

    //超時降級
    static HystrixCommand.Setter TIMEOUT = HystrixCommand.Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("HystrixService"))
            .andCommandPropertiesDefaults(HystrixCommandProperties.Setter()
                    .withExecutionTimeoutEnabled(true)
                    .withExecutionTimeoutInMilliseconds(3000)
            );



    //線程池隔離
    static HystrixCommand.Setter THREAD = HystrixCommand.Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("HystrixService"))
            .andThreadPoolPropertiesDefaults(HystrixThreadPoolProperties.Setter()
                    .withCoreSize(20)
                    .withMaxQueueSize(1000)
                    .withQueueSizeRejectionThreshold(800)
                    .withKeepAliveTimeMinutes(2));



    //信號量隔離
    static HystrixCommand.Setter SEMAPHORE = HystrixCommand.Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("HystrixService"))
            .andCommandPropertiesDefaults(HystrixCommandProperties.Setter()
                    .withExecutionIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.SEMAPHORE)
                    .withExecutionIsolationSemaphoreMaxConcurrentRequests(2)
            );

代碼地址: https://gitee.com/gzgyc/cloud-demo.git 里的hystrix-demo


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM