springcloud優雅停止上下線與熔斷


SpringCloud 服務優雅上下線 

Spring Boot 框架使用“約定大於配置”的特性,優雅流暢的開發過程,應用部署啟動方式也很優雅。
但是我們通常使用的停止應用的方式是 kill -9 <pid> ,即使我們編寫腳本,還是顯得有些粗魯。
這樣的應用停止方式,在停止的那一霎那,應用中正在處理的業務邏輯會被中斷,導致產生業務異常情形。
這種情況如何避免,本文介紹的優雅停機,將完美解決該問題。

什么叫優雅停機?

簡單說就是在對應用進程發送停止指令之后,能保證正在執行的業務操作不受影響。應用接收到停止指令之后的步驟應該是,停止接收訪問請求,等待已經接收到的請求處理完成,並能成功返回,這時才真正停止應用。

  • Spring Cloud 微服務到注冊中心的注冊操作是通過 Rest 接口調用的。
  • 它不能像 ZooKeeper 那樣,有問題節點反饋及時生效。
  • 也不能像 Redis 那么快的去輪詢,太耗費資源。

如下圖:

 

 

優雅的三個要求:

1) ServiceA 下線一台實例后,Zuul 網關的調用不能失敗;
2) ServiceB 下線一台實例后,ServiceA 的 Feign 調用不能失敗;
3) 其中一個微服務上線或下線,Eureka 及其它微服務能夠快速感知;

說白了就一件事,怎樣盡量縮短服務下線后 Zuul 和其他被依賴服務的發現時間,並在這段時間內保證請求不失敗

解決時間問題

影響因素

 

三級緩存

 

緩存相關配置

  

配置 默認 說明
eureka.server.useReadOnlyResponseCache true Client從readOnlyCacheMap更新數據,false則跳過readOnlyCacheMap直接從readWriteCacheMap更新
eureka.server.responsecCacheUpdateIntervalMs 30000 readWriteCacheMap更新至readOnlyCacheMap周期,默認30s
eureka.server.evictionIntervalTimerInMs 60000 清理未續約節點(evict)周期,默認60s
eureka.instance.leaseExpirationDurationInSeconds 90 清理未續約節點超時時間,默認90s

關鍵類

類名 說明
com.netflix.eureka.registry.AbstractInstanceRegistry 保存服務注冊信息,持有registry和responseCache成員變量
com.netflix.eureka.registry.ResponseCacheImpl 持有readWriteCacheMap和readOnlyCacheMap成員變量

 

1) Eureka 的兩層緩存問題 (待確認)

Eureka Server 默認有兩個緩存,一個是 ReadWriteMap 讀寫緩存,另一個是 ReadOnlyMap 只讀緩存。
Eureka Server 存在三個變量:(registry、readWriteCacheMap、readOnlyCacheMap)保存服務注冊信息,默認情況下定時任務每 30s 將 readWriteCacheMap 同步至 readOnlyCacheMap,每 60s 清理超過 90s 未續約的節點,Eureka Client 每 30s 從 readOnlyCacheMap 更新服務注冊信息,而 UI 則從 registry 更新服務注冊信息。

有服務提供者注冊注銷服務或者維持心跳時,會修改 ReadWriteMap。
當有服務調用者查詢服務實例列表時,默認會從 ReadOnlyMap 讀取(這個在原生 Eureka 可以配置,SpringCloud Eureka 中不能配置,一定會啟用 ReadOnlyMap 讀取),
這樣可以減少 ReadWriteMap 讀寫鎖的爭用,增大吞吐量。
EurekaServer 定時把數據從 ReadWriteMap 更新到 ReadOnlyMap 中。
Eureka注冊中心收到注銷請求后,會先將信息更新到讀寫緩存中。
注冊中心有個定時任務會定時(默認值是30s)將讀寫緩存中的數據同步到只讀緩存中。

shouldUseReadOnlyResponseCache 讀取的是 eureka.server.use-read-only-response-cache 參數
responseCacheUpdateIntervalMs 讀取的是 eureka.server.response-cache-update-interval-ms 參數
responseCacheAutoExpirationInSeconds 讀取的是 eureka.server.response-cache-update-interval-ms 參數

eureka server對rest api提供了多級緩存,第一層是readOnlyCacheMap,然后是readWriteCacheMap,最后如果readWriteCacheMap讀取不到,然后就從registry進行讀取。其中readOnlyCacheMap會定時從readWriteCacheMap更新數據,而readWriteCacheMap有自己的過期時間,過期后自動從loader加載新數據。

 

2) 心跳時間

服務提供者注冊服務后,會定時心跳。這個根據服務提供者的 Eureka 配置中的服務刷新時間決定。還有個配置是服務過期時間,這個配置在服務提供者配置但是在 EurekaServer 使用了,但是默認配置 EurekaServer 不會啟用這個字段。需要配置好 EurekaServer 的掃描失效時間,才會啟用 EurekaServer 的主動失效機制。在這個機制啟用下:每個服務提供者會發送自己服務過期時間上去,EurekaServer 會定時檢查每個服務過期時間和上次心跳時間,如果在過期時間內沒有收到過任何一次心跳,同時沒有處於保護模式下,則會將這個實例從 ReadWriteMap 中去掉。

3) 調用者服務從 Eureka 拉列表的輪訓間隔

由於eureka注冊中心沒有通知的功能,只能由節點自己發起刷新請求,所以修改狀態后,需要等到相關節點下一次刷新后才會生效。
節點刷新是通過定時任務實現的,源碼在com.netflix.discovery.DiscoveryClient中,並且任務是在構造方法中初始化的,還不能自己手動觸發。
CloudEurekaClient中有一個刷新的方法,發布一個心跳事件,但這個方法是protected,沒法通過實例調用,並且依賴於心跳事件。
應用節點默認刷新事件是60秒一次,時間也不算太長,所以動態停用節點后再60秒內生效,應該是在能接受的范圍吧,並且這個時間還能配置。

4) Ribbon 緩存

Ribbon 去請求其它微服務時的“負載均衡”列表,有緩存時間,過期后才會更新。

解決方式

1) 禁用 Eureka 的 ReadOnlyMap 緩存 (Eureka 端)

eureka.server.use-read-only-response-cache: false

2) 啟用主動失效,並且每次主動失效檢測間隔為 3s (Eureka 端)

eureka.server.eviction-interval-timer-in-ms: 3000

像 eureka.server.responseCacheUpdateInvervalMs和 eureka.server.responseCacheAutoExpirationInSeconds在啟用了主動失效后其實沒什么用了。默認的 180s 真夠把人給急瘋的。

3) 調整服務過期時間 (服務提供方)

eureka.instance.lease-expiration-duration-in-seconds: 15

超過這個時間沒有接收到心跳 Eureka Server 就會將這個實例剔除。

注意:Eureka Server 一定要設置 eureka.server.eviction-interval-timer-in-ms ,否則這個配置無效,這個配置一般為服務刷新時間配置的三倍。默認 90s!

4) 服務刷新時間配置,每隔這個時間會主動心跳一次 (服務提供方)

eureka.instance.lease-renewal-interval-in-seconds: 5

服務心跳間隔時間, 默認 30s

5) 拉服務列表時間間隔 (客戶端)

eureka.client.registryFetchIntervalSeconds: 5

客戶端去 Eureka 注冊表拉取服務列表的時間間隔設置,默認 30s

6) ribbon 刷新時間 (客戶端)

ribbon.ServerListRefreshInterval: 5000

注意 ribbon 也有緩存,ribbon 中此參數可以用來調整刷新server list的時間間隔,默認 30s

這些超時時間相互影響,三個地方都需要配置,一不小心就會出現服務不下線,服務不上線的囧境。不得不說 SpringCloud 的這套默認參數很復雜,一定要小心謹慎地修改。

重試機制

那么一台服務器下線,最長的不可用(指請求會落到下線的服務器上,請求失敗)的時間是多少呢?
趕的巧的話,這個基本時間就是 eureka.client.registryFetchIntervalSeconds+ribbon.ServerListRefreshInterval, 大約是 8秒的時間。如果算上服務端主動失效的時間,這個時間會增加到 11秒

如果你只有兩個實例,極端情況下服務上線的發現時間也需要 11 秒,那就是 22 秒的時間。

理想情況下,在這 11 秒之間,請求是失敗的。假如你的 QPS 是 1000,部署了四個節點,那么在 11 秒中失敗的請求數量會是 1000 / 4 * 11 = 2750,這是不可接受的。所以我們要引入重試機制

SpringCloud 引入重試還是比較簡單的。但不是配置一下就可以的,既然用了重試,那么就還需要控制超時。可以按照以下的步驟:

 

1) 引入 pom (千萬別忘了哦)

org.springframework.retryspring-retry

2) 加入配置

ribbon.OkToRetryOnAllOperations:true # (是否所有操作都重試,若false則僅get請求重試)
ribbon.MaxAutoRetriesNextServer:3 # (重試負載均衡其他實例最大重試次數,不含首次實例)
ribbon.MaxAutoRetries:1 # (同一實例最大重試次數,不含首次調用)
ribbon.ReadTimeout:30000
ribbon.ConnectTimeout:3000
ribbon.retryableStatusCodes:404,500,503 # (那些狀態進行重試)
spring.cloud.loadbalancer.retry.enable:true # (重試開關)

3) 發布系統

 

OK, 機制已經解釋清楚,但是實踐起來還是很繁雜的,讓人焦躁。比如有一個服務有兩個實例,我要一台一台的去發布,在發布第二台之前,起碼要等上 11 秒。如果手速太快,那就是災難。所以一個配套的發布系統是必要的。

首先可以通過 rest 請求去請求 Eureka,主動去隔離一台實例,多了這一步,可以減少至少 3 秒服務不可用的時間(還是比較划算的)。

然后通過打包工具打包,推包。依次上線替換。

附錄 —— Spring Boot 項目優雅停止

默認的 Spring Boot 項目收到 kill 發來的 SIGTERM 信號后,會中斷所有正在進行的請求,這樣有些粗魯。

Spring Boot 優雅停止參考:https://github.com/erdanielli/spring-boot-graceful-shutdown/tree/master/src/main/java/com/github/erdanielli/boot/shutdown

package com.sudheera.playground.spring.testgracefullshutdown.config;

import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import org.apache.catalina.connector.Connector;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.springframework.boot.web.embedded.tomcat.TomcatConnectorCustomizer;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.stereotype.Component;

/**
 * Custom Tomcat protocol handler's executor service shutdown listner implementation to avoid spring application context
 * to shutdown before shutting down the tomcat server threads. This bean will do following when the application context
 * closed event occurred.
 *
 * 1. Pausing connector to stop accepting new connections
 * 2. Invoke shutdown of the ThreadPoolExecutor used in the Tomcat protocol handler
 * 3. Await until ThreadPoolExecutor is completely shutdown
 *
 * Read : https://github.com/spring-projects/spring-boot/issues/4657 for more information regarding the issue, this
 * implementation is based on `wilkinsona`s proposed solution in the issue thread
 */
@Component
public class TomcatGracefulShutdownListener implements TomcatConnectorCustomizer, ApplicationListener<ContextClosedEvent> {

    private static final Logger log = LoggerFactory.getLogger(TomcatGracefulShutdownListener.class);
    private static final int AWAIT_SECONDS = 100;

    private volatile Connector connector;

    @Override
    public void customize(Connector connector) {
        this.connector = connector;
    }

    @Override
    public void onApplicationEvent(ContextClosedEvent event) {
        connector.pause();
        Executor executor = connector.getProtocolHandler().getExecutor();
        if (executor == null) {
            return;
        }

        if (!(executor instanceof ThreadPoolExecutor)) {
            log.warn("{} is not an instance of ThreadPoolExecutor class, Can't ensure graceful shutdown..!!", executor.getClass());
            return;
        }

        ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) executor;
        log.info("Shutting down Tomcat thread pool executor. Active thread count : {}", threadPoolExecutor.getActiveCount());
        threadPoolExecutor.shutdown();
        try {
            if (threadPoolExecutor.awaitTermination(AWAIT_SECONDS, TimeUnit.SECONDS)) {
                log.info("Tomcat shutdown gracefully.!");
                return;
            }

            threadPoolExecutor.shutdownNow();
            if (!threadPoolExecutor.awaitTermination(AWAIT_SECONDS, TimeUnit.SECONDS)) {
                log.warn("Tomcat thread pool executor did not terminate. Active thread count : {}", threadPoolExecutor.getActiveCount());
            }

        } catch (InterruptedException ex) {
            log.error("Got interrupted while waiting for graceful shutdown", ex);
            Thread.currentThread().interrupt();
        }
    }
}

 

Spring Boot 項目優雅停止

當我們流量請求到此接口執行業務邏輯的時候,若服務端此時執行關機 (kill),spring boot 默認情況會直接關閉容器(tomcat 等),導致此業務邏輯執行失敗。在一些業務場景下:會出現數據不一致的情況,事務邏輯不會回滾。

首先來介紹下什么是優雅地停止,簡而言之,就是對應用進程發送停止指令之后,能保證正在執行的業務操作不受影響,可以繼續完成已有請求的處理,但是停止接受新請求。

Spring Boot 2.3 中增加了新特性優雅停止,目前 Spring Boot 內置的四個嵌入式 Web 服務器(Jetty、Reactor Netty、Tomcat 和 Undertow)以及反應式和基於 Servlet 的 Web 應用程序都支持優雅停止。

下面,我們先用新版本嘗試下:

Spring Boot 2.3 優雅停止

首先創建一個 Spring Boot 的 Web 項目,版本選擇 2.3.0.RELEASE,Spring Boot 2.3.0.RELEASE 版本內置的 Tomcat 為 9.0.35。

然后需要在 application.yml 中添加一些配置來啟用優雅停止的功能:

# 開啟優雅停止 Web 容器,默認為 IMMEDIATE:立即停止
server:
  shutdown: graceful

# 最大等待時間
spring:
  lifecycle:
    timeout-per-shutdown-phase: 30s

其中,平滑關閉內置的 Web 容器(以 Tomcat 為例)的入口代碼在 org.springframework.boot.web.embedded.tomcat 的 GracefulShutdown 里,大概邏輯就是先停止外部的所有新請求,然后再處理關閉前收到的請求,有興趣的可以自己去看下。

內嵌的 Tomcat 容器平滑關閉的配置已經完成了,那么如何優雅關閉 Spring 容器了,就需要 Actuator 來實現 Spring 容器的關閉了。

然后加入 actuator 依賴,依賴如下所示:

    org.springframework.boot
    spring-boot-starter-actuator

然后接着再添加一些配置來暴露 actuator 的 shutdown 接口:

# 暴露 shutdown 接口
management:
  endpoint:
    shutdown:
      enabled: true
  endpoints:
    web:
      exposure:
        include: shutdown

其中通過 Actuator 關閉 Spring 容器的入口代碼在 org.springframework.boot.actuate.context 包下 ShutdownEndpoint 類中,主要的就是執行 doClose() 方法關閉並銷毀 applicationContext,有興趣的可以自己去看下。

配置搞定后,然后在 controller 包下創建一個 WorkController 類,並有一個 work 方法,用來模擬復雜業務耗時處理流程,具體代碼如下:

@RestController
public class WorkController {

    @GetMapping("/work")
    public String work() throws InterruptedException {
        // 模擬復雜業務耗時處理流程
        Thread.sleep(10 * 1000L);
        return "success";
    }
}

然后,我們啟動項目,先用 Postman 請求 http://localhost:8080/work 處理業務:

然后在這個時候,調用 http://localhost:8080/actuator/shutdown 就可以執行優雅地停止,返回結果如下:

{
    "message": "Shutting down, bye..."
}

如果在這個時候,發起新的請求 http://localhost:8080/work, 會沒有反應

再回頭看第一個請求,返回了結果:success。

其中有幾條服務日志如下:

2020-05-20 23:05:15.163  INFO 102724 --- [     Thread-253] o.s.b.w.e.tomcat.GracefulShutdown        : Commencing graceful shutdown. Waiting for active requests to complete
2020-05-20 23:05:15.287  INFO 102724 --- [tomcat-shutdown] o.s.b.w.e.tomcat.GracefulShutdown        : Graceful shutdown complete
2020-05-20 23:05:15.295  INFO 102724 --- [     Thread-253] o.s.s.concurrent.ThreadPoolTaskExecutor  : Shutting down ExecutorService 'applicationTaskExecutor'

從日志中也可以看出來,當調用 shutdown 接口的時候,會先等待請求處理完畢后再優雅地停止。

到此為止,Spring Boot 2.3 的優雅關閉就講解完了,是不是很簡單呢?如果是在之前不支持優雅關閉的版本如何去做呢?

不同 web 容器優雅停機行為區別

容器停機行為取決於具體的 web 容器行為

web 容器名稱 行為說明
tomcat 9.0.33+ 停止接收請求,客戶端新請求等待超時。
Reactor Netty 停止接收請求,客戶端新請求等待超時。
Undertow 停止接收請求,客戶端新請求直接返回 503。

 

Spring Boot 舊版本優雅停止

在這里介紹 GitHub 上 issue 里 Spring Boot 開發者提供的一種方案:

選取的 Spring Boot 版本為 2.2.6.RELEASE,首先要實現 TomcatConnectorCustomizer 接口,該接口是自定義 Connector 的回調接口:

@FunctionalInterface
public interface TomcatConnectorCustomizer {

    void customize(Connector connector);
}

除了定制 Connector 的行為,還要實現 ApplicationListener 接口,因為要監聽 Spring 容器的關閉事件,即當前的 ApplicationContext 執行 close() 方法,這樣我們就可以在請求處理完畢后進行 Tomcat 線程池的關閉,具體的實現代碼如下:

@Bean
public GracefulShutdown gracefulShutdown() {
    return new GracefulShutdown();
}

private static class GracefulShutdown implements TomcatConnectorCustomizer, ApplicationListener {
    private static final Logger log = LoggerFactory.getLogger(GracefulShutdown.class);

    private volatile Connector connector;

    @Override
    public void customize(Connector connector) {
        this.connector = connector;
    }

    @Override
    public void onApplicationEvent(ContextClosedEvent event) {
        this.connector.pause();
        Executor executor = this.connector.getProtocolHandler().getExecutor();
        if (executor instanceof ThreadPoolExecutor) {
            try {
                ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) executor;
                threadPoolExecutor.shutdown();
                if (!threadPoolExecutor.awaitTermination(30, TimeUnit.SECONDS)) {
                    log.warn("Tomcat thread pool did not shut down gracefully within 30 seconds. Proceeding with forceful shutdown");
                }
            } catch (InterruptedException ex) {
                Thread.currentThread().interrupt();
            }
        }
    }
}

有了定制的 Connector 回調,還需要在啟動過程中添加到內嵌的 Tomcat 容器中,然后等待監聽到關閉指令時執行,addConnectorCustomizers 方法可以把定制的 Connector 行為添加到內嵌的 Tomcat 中,具體代碼如下:

@Bean
public ConfigurableServletWebServerFactory tomcatCustomizer() {
    TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
    factory.addConnectorCustomizers(gracefulShutdown());
    return factory;
}

到此為止,內置的 Tomcat 容器平滑關閉的操作就完成了,Spring 容器優雅停止上面已經說過了,再次就不再贅述了。

通過測試,同樣可以達到上面那樣優雅停止的效果。

總結

本文主要講解了 Spring Boot 2.3 版本和舊版本的優雅停止,避免強制停止導致正在處理的業務邏輯會被中斷,進而導致產生業務異常的情形。

另外使用 Actuator 的同時要注意安全問題,比如可以通過引入 security 依賴,打開安全限制並進行身份驗證,設置單獨的 Actuator 管理端口並配置只對內網開放等。

本文的完整代碼在 https://github.com/wupeixuan/SpringBoot-Learn 的 graceful-shutdown 目錄下。

最好的關系就是互相成就,大家的在看、轉發、留言三連就是我創作的最大動力。

參考

https://github.com/spring-projects/spring-boot/issues/4657
https://github.com/wupeixuan/SpringBoot-Learn

 

Hystrix 熔斷器

雪崩效應

雪崩效應是一種因服務提供者的不可用,導致服務調用者的不可用,並將不可用逐漸放大的過程。

當一切正常時,請求流如下所示:

 

當有后端系統有問題時,它會阻止所有用戶請求:

 

 

隨着流量的增大,單個后端依賴性變得有問題,這可能導致所有服務器上的所有資源在幾秒鍾內變得飽和。
應用程序中可能會導致網絡請求通過網絡或客戶端庫傳播的每個點都是潛在故障的根源。
比故障更糟糕的是,這些應用程序還會導致服務之間的等待時間增加,而且會進一步影響到備份隊列,線程和其他系統資源,從而導致整個系統出現更多級聯故障。

當通過第三方客戶端執行網絡訪問時,這些問題會更加嚴重。“第三方”是一個隱藏了實施細節的“黑匣子”,可以隨時更改,並且每個客戶端庫的網絡或資源配置都不相同,並且通常難以監視和監控。

熔斷

如果某個目標服務調用慢或者有大量超時,此時,熔斷該服務的調用,對於后續調用請求,不在繼續調用目標服務,直接返回,快速釋放資源。如果目標服務情況好轉則恢復調用。

熔斷器是位於線程池之前的組件。用戶請求某一服務之后,Hystrix會先經過熔斷器,此時如果熔斷器的狀態是打開(跳起),則說明已經熔斷,這時將直接進行降級處理,不會繼續將請求發到線程池。熔斷器相當於在線程池之前的一層屏障。每個熔斷器默認維護10個bucket ,每秒創建一個bucket ,每個blucket記錄成功,失敗,超時,拒絕的次數。當有新的bucket被創建時,最舊的bucket會被拋棄。

熔斷器的狀態機:

 

  • Closed:熔斷器關閉狀態,調用失敗次數積累,到了閾值(或一定比例)則啟動熔斷機制;
  • Open:熔斷器打開狀態,此時對下游的調用都內部直接返回錯誤,不走網絡,但設計了一個時鍾選項,默認的時鍾達到了一定時間(這個時間一般設置成平均故障處理時間,也就是MTTR),到了這個時間,進入半熔斷狀態;
  • Half-Open:半熔斷狀態,允許定量的服務請求,如果調用都成功(或一定比例)則認為恢復了,關閉熔斷器,否則認為還沒好,又回到熔斷器打開狀態;

Hystrix的工作原理:

  • 防止任何單個依賴項耗盡所有容器(例如Tomcat)用戶線程。
  • 減少負載並快速失敗,而不是排隊。
  • 在可行的情況下提供備用方法,以保護用戶免受故障的影響。
  • 使用隔離技術(例如隔板,泳道和斷路器模式)來限制任何一種依賴關系的影響。
  • 通過近實時指標,監控和警報優化故障發現時間
  • 通過在 Hystrix 的大多數方面中以低延遲傳播配置更改來優化恢復時間,並支持動態屬性更改,這使您可以通過低延遲反饋回路進行實時操作修改。
  • 防止整個依賴項客戶端執行失敗,而不僅僅是網絡流量失敗。

流程

 

Fallback 回退

  • 執行超時
  • 執行過程拋出異常
  • 斷路器打開狀態
  • 線程池拒絕(池滿后的拒絕策略)
  • 信號量拒絕(信號量耗完)

電路打開和閉合的精確方式

  • 如果電路上的音量達到某個閾值(HystrixCommandProperties.circuitBreakerRequestVolumeThreshold())…
  • 並且錯誤百分比超過閾值誤差百分比(HystrixCommandProperties.circuitBreakerErrorThresholdPercentage())…
  • 然后,斷路器從轉換CLOSED為OPEN。
  • 當它斷開時,它會使針對該斷路器的所有請求短路。
  • 經過一段時間(HystrixCommandProperties.circuitBreakerSleepWindowInMilliseconds())后,下一個單個請求被允許通過(這是HALF-OPEN狀態)。如果請求失敗,則斷路器將OPEN在睡眠窗口持續時間內返回到該狀態。如果請求成功,斷路器將切換到,CLOSED並且1.中的邏輯將再次接管。

配置參數

參數 作用 備注
circuitBreaker.errorThresholdPercentage 失敗率達到多少百分比后熔斷 默認值:50,
    主要根據依賴重要性進行調整
circuitBreaker.forceClosed 是否強制關閉熔斷 如果是強依賴,應該設置為true
circuitBreaker.requestVolumeThreshold 熔斷觸發的最小個數/10s 默認值:20
circuitBreaker.sleepWindowInMilliseconds 熔斷多少毫秒后去嘗試請求 默認值:5000
commandKey 使用自定義命令名稱(可以配置文件中針對此名稱進行配置) 默認值:當前執行方法名
coreSize 線程池coreSize 默認值:10
execution.isolation.semaphore.maxConcurrentRequests 信號量最大並發度 SEMAPHORE模式有效,默認值:10
execution.isolation.strategy 隔離策略,有THREAD和SEMAPHORE 默認使用THREAD模式,以下幾種可以使用SEMAPHORE模式:
    只想控制並發度
    外部的方法已經做了線程隔離
    調用的是本地方法或者可靠度非常高、耗時特別小的方法(如medis)
execution.isolation.thread.interruptOnTimeout 是否打開超時線程中斷 THREAD模式有效
execution.isolation.thread.timeoutInMilliseconds 超時時間 默認值:1000 毫秒
    在THREAD模式下,達到超時時間,可以中斷
    在SEMAPHORE模式下,會等待執行完成后,再去判斷是否超時
execution.timeout.enabled 是否打開超時  
fallback.isolation.semaphore.maxConcurrentRequests fallback最大並發度 默認值:10
groupKey 表示所屬的group,一個group共用線程池 默認值:getClass().getSimpleName();
maxQueueSize 請求等待隊列 默認值:-1
    如果使用正數,隊列將從SynchronizeQueue改為LinkedBlockingQueue
hystrix.command.default.metrics.rollingStats.timeInMilliseconds 設置統計的時間窗口值的,毫秒值 circuit break 的打開會根據1個rolling window的統計來計算。若rolling window被設為10000毫秒,則rolling window會被分成n個buckets,每個bucket包含success,failure,timeout,rejection的次數的統計信息。默認10000
hystrix.command.default.metrics.rollingStats.numBuckets 設置一個rolling window被划分的數量  
hystrix.command.default.metrics.healthSnapshot.intervalInMilliseconds 記錄health 快照(用來統計成功和錯誤綠)的間隔 默認500ms

 

 

當您使用Hystrix封裝每個基礎依賴項時,如上圖所示的體系結構將更改為類似於下圖。每個依賴項都是相互隔離的,受到延遲時發生飽和的資源的限制,並包含回退邏輯,該邏輯決定了在依賴項中發生任何類型的故障時做出什么響應:

 

ribbon:
  ReadTimeout: 30000
  ConnectTimeout: 30000
feign:
  hystrix:
    # feign熔斷器開關
    enabled: true

hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            #斷路器的超時時間ms,默認1000
            timeoutInMilliseconds: 30000
      circuitBreaker:
        #當在配置時間窗口內達到此數量的失敗后,進行短路     
        requestVolumeThreshold: 20
        #出錯百分比閾值,當達到此閾值后,開始短路。默認50%)
        errorThresholdPercentage: 50%
        #短路多久以后開始嘗試是否恢復,默認5s)-單位ms
        sleepWindowInMilliseconds: 500
hystrix.command.default和hystrix.threadpool.default中的default為默認CommandKey

Command Properties
Execution相關的屬性的配置:
hystrix.command.default.execution.isolation.strategy 隔離策略,默認是Thread, 可選Thread|Semaphore

hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds 命令執行超時時間,默認1000ms

hystrix.command.default.execution.timeout.enabled 執行是否啟用超時,默認啟用true
hystrix.command.default.execution.isolation.thread.interruptOnTimeout 發生超時是是否中斷,默認true
hystrix.command.default.execution.isolation.semaphore.maxConcurrentRequests 最大並發請求數,默認10,該參數當使用ExecutionIsolationStrategy.SEMAPHORE策略時才有效。如果達到最大並發請求數,請求會被拒絕。理論上選擇semaphore size的原則和選擇thread size一致,但選用semaphore時每次執行的單元要比較小且執行速度快(ms級別),否則的話應該用thread。
semaphore應該占整個容器(tomcat)的線程池的一小部分。
Fallback相關的屬性
這些參數可以應用於Hystrix的THREAD和SEMAPHORE策略

hystrix.command.default.fallback.isolation.semaphore.maxConcurrentRequests 如果並發數達到該設置值,請求會被拒絕和拋出異常並且fallback不會被調用。默認10
hystrix.command.default.fallback.enabled 當執行失敗或者請求被拒絕,是否會嘗試調用hystrixCommand.getFallback() 。默認true
Circuit Breaker相關的屬性
hystrix.command.default.circuitBreaker.enabled 用來跟蹤circuit的健康性,如果未達標則讓request短路。默認true
hystrix.command.default.circuitBreaker.requestVolumeThreshold 一個rolling window內最小的請求數。如果設為20,那么當一個rolling window的時間內(比如說1個rolling window是10秒)收到19個請求,即使19個請求都失敗,也不會觸發circuit break。默認20
hystrix.command.default.circuitBreaker.sleepWindowInMilliseconds 觸發短路的時間值,當該值設為5000時,則當觸發circuit break后的5000毫秒內都會拒絕request,也就是5000毫秒后才會關閉circuit。默認5000
hystrix.command.default.circuitBreaker.errorThresholdPercentage錯誤比率閥值,如果錯誤率>=該值,circuit會被打開,並短路所有請求觸發fallback。默認50
hystrix.command.default.circuitBreaker.forceOpen 強制打開熔斷器,如果打開這個開關,那么拒絕所有request,默認false
hystrix.command.default.circuitBreaker.forceClosed 強制關閉熔斷器 如果這個開關打開,circuit將一直關閉且忽略circuitBreaker.errorThresholdPercentage
Metrics相關參數
hystrix.command.default.metrics.rollingStats.timeInMilliseconds 設置統計的時間窗口值的,毫秒值,circuit break 的打開會根據1個rolling window的統計來計算。若rolling window被設為10000毫秒,則rolling window會被分成n個buckets,每個bucket包含success,failure,timeout,rejection的次數的統計信息。默認10000
hystrix.command.default.metrics.rollingStats.numBuckets 設置一個rolling window被划分的數量,若numBuckets=10,rolling window=10000,那么一個bucket的時間即1秒。必須符合rolling window % numberBuckets == 0。默認10
hystrix.command.default.metrics.rollingPercentile.enabled 執行時是否enable指標的計算和跟蹤,默認true
hystrix.command.default.metrics.rollingPercentile.timeInMilliseconds 設置rolling percentile window的時間,默認60000
hystrix.command.default.metrics.rollingPercentile.numBuckets 設置rolling percentile window的numberBuckets。邏輯同上。默認6
hystrix.command.default.metrics.rollingPercentile.bucketSize 如果bucket size=100,window=10s,若這10s里有500次執行,只有最后100次執行會被統計到bucket里去。增加該值會增加內存開銷以及排序的開銷。默認100
hystrix.command.default.metrics.healthSnapshot.intervalInMilliseconds 記錄health 快照(用來統計成功和錯誤綠)的間隔,默認500ms
Request Context 相關參數
hystrix.command.default.requestCache.enabled 默認true,需要重載getCacheKey(),返回null時不緩存
hystrix.command.default.requestLog.enabled 記錄日志到HystrixRequestLog,默認true

Collapser Properties 相關參數
hystrix.collapser.default.maxRequestsInBatch 單次批處理的最大請求數,達到該數量觸發批處理,默認Integer.MAX_VALUE
hystrix.collapser.default.timerDelayInMilliseconds 觸發批處理的延遲,也可以為創建批處理的時間+該值,默認10
hystrix.collapser.default.requestCache.enabled 是否對HystrixCollapser.execute() and HystrixCollapser.queue()的cache,默認true

ThreadPool 相關參數
線程數默認值10適用於大部分情況(有時可以設置得更小),如果需要設置得更大,那有個基本得公式可以follow:
requests per second at peak when healthy × 99th percentile latency in seconds + some breathing room
每秒最大支撐的請求數 (99%平均響應時間 + 緩存值)
比如:每秒能處理1000個請求,99%的請求響應時間是60ms,那么公式是:
10000.060+0.012)

基本得原則時保持線程池盡可能小,他主要是為了釋放壓力,防止資源被阻塞。
當一切都是正常的時候,線程池一般僅會有1到2個線程激活來提供服務

hystrix.threadpool.default.coreSize 並發執行的最大線程數,默認10
hystrix.threadpool.default.maxQueueSize BlockingQueue的最大隊列數,當設為-1,會使用SynchronousQueue,值為正時使用LinkedBlcokingQueue。該設置只會在初始化時有效,之后不能修改threadpool的queue size,除非reinitialising thread executor。默認-1。
hystrix.threadpool.default.queueSizeRejectionThreshold 即使maxQueueSize沒有達到,達到queueSizeRejectionThreshold該值后,請求也會被拒絕。因為maxQueueSize不能被動態修改,這個參數將允許我們動態設置該值。if maxQueueSize == -1,該字段將不起作用
hystrix.threadpool.default.keepAliveTimeMinutes 如果corePoolSize和maxPoolSize設成一樣(默認實現)該設置無效。如果通過plugin(https://github.com/Netflix/Hystrix/wiki/Plugins)使用自定義實現,該設置才有用,默認1.
hystrix.threadpool.default.metrics.rollingStats.timeInMilliseconds 線程池統計指標的時間,默認10000
hystrix.threadpool.default.metrics.rollingStats.numBuckets 將rolling window划分為n個buckets,默認10

 


免責聲明!

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



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