SpringCloud--熔斷器:Hystricx

1.1 Hystrix介紹

Hystrix的設(shè)計(jì)原則是什么?

l 資源隔離(線程池隔離和信號(hào)量隔離)機(jī)制:限制調(diào)用分布式服務(wù)的資源使用,某一個(gè)調(diào)用的服務(wù)出現(xiàn)問(wèn)題不會(huì)影響其它服務(wù)調(diào)用。

l 限流機(jī)制:限流機(jī)制主要是提前對(duì)各個(gè)類(lèi)型的請(qǐng)求設(shè)置最高的QPS閾值,若高于設(shè)置的閾值則對(duì)該請(qǐng)求直接返回,不再調(diào)用后續(xù)資源。

l 熔斷機(jī)制:當(dāng)失敗率達(dá)到閥值自動(dòng)觸發(fā)降級(jí)(如因網(wǎng)絡(luò)故障、超時(shí)造成的失敗率真高),熔斷器觸發(fā)的快速失敗會(huì)進(jìn)行快速恢復(fù)。

l 降級(jí)機(jī)制:超時(shí)降級(jí)、資源不足時(shí)(線程或信號(hào)量)降級(jí) 、運(yùn)行異常降級(jí)等,降級(jí)后可以配合降級(jí)接口返回托底數(shù)據(jù)。

l 緩存支持:提供了請(qǐng)求緩存、請(qǐng)求合并實(shí)現(xiàn)

l 通過(guò)近實(shí)時(shí)的統(tǒng)計(jì)/監(jiān)控/報(bào)警功能,來(lái)提高故障發(fā)現(xiàn)的速度

l 通過(guò)近實(shí)時(shí)的屬性和配置熱修改功能,來(lái)提高故障處理和恢復(fù)的速度

1.2 Hystrix整體工作流程

整個(gè)流程可以大致歸納為如下幾個(gè)步驟:

l 創(chuàng)建HystrixCommand或者HystrixObservableCommand對(duì)象

l 執(zhí)行 Command

l 檢查請(qǐng)求結(jié)果是否被緩存

l 檢查是否開(kāi)啟了短路器

l 檢查 線程池/隊(duì)列/semaphore 是否已經(jīng)滿(mǎn)

l 執(zhí)行 HystrixObservableCommand.construct() or HystrixCommand.run()

l 計(jì)算短路健康狀況

l 調(diào)用fallback降級(jí)機(jī)制

l 返回依賴(lài)請(qǐng)求的真正結(jié)果

1.3 Hystrix特性

1.3.1 資源隔離

l 說(shuō)明:在Hystrix中, 主要通過(guò)線程池來(lái)實(shí)現(xiàn)資源隔離. 通常在使用的時(shí)候我們會(huì)根據(jù)調(diào)用的遠(yuǎn)程服務(wù)劃分出多個(gè)線程池. 例如調(diào)用產(chǎn)品服務(wù)的Command放入A線程池, 調(diào)用賬戶(hù)服務(wù)的Command放入B線程池. 這樣做的主要優(yōu)點(diǎn)是運(yùn)行環(huán)境被隔離開(kāi)了. 這樣就算調(diào)用服務(wù)的代碼存在bug或者由于其他原因?qū)е伦约核诰€程池被耗盡時(shí), 不會(huì)對(duì)系統(tǒng)的其他服務(wù)造成影響. 但是帶來(lái)的代價(jià)就是維護(hù)多個(gè)線程池會(huì)對(duì)系統(tǒng)帶來(lái)額外的性能開(kāi)銷(xiāo). 如果是對(duì)性能有嚴(yán)格要求而且確信自己調(diào)用服務(wù)的客戶(hù)端代碼不會(huì)出問(wèn)題的話(huà), 可以使用Hystrix的信號(hào)模式(Semaphores)來(lái)隔離資源

l Command執(zhí)行方式

execute():以同步堵塞方式執(zhí)行 run()。調(diào)用 execute() 后,hystrix先創(chuàng)建一個(gè)新線程運(yùn)行run(),接著調(diào)用程序要在 execute() 調(diào)用處一直堵塞著,直到 run() 運(yùn)行完成。

queue():以異步非堵塞方式執(zhí)行 run() 。調(diào)用 queue() 就直接返回一個(gè) Future 對(duì)象,同時(shí)hystrix創(chuàng)建一個(gè)新線程運(yùn)行 run(),調(diào)用程序通過(guò) Future.get() 拿到 run() 的返回結(jié)果,而Future.get() 是堵塞執(zhí)行的。

observe():立即執(zhí)行,即事件subscribe()完成注冊(cè)前執(zhí)行 run()/construct() 。

第一步是事件注冊(cè)前,先調(diào)用 observe() 自動(dòng)觸發(fā)執(zhí)行 run()/construct()(如果繼承的是HystrixCommand,hystrix將創(chuàng)建新線程非堵塞執(zhí)行run();如果繼承的是HystrixObservableCommand,將以調(diào)用程序線程堵塞執(zhí)行construct()),

第二步是從 observe() 返回后調(diào)用程序調(diào)用 subscribe() 完成事件注冊(cè),如果 run()/construct() 執(zhí)行成功則觸發(fā) onNext() 和 onCompleted() ,如果執(zhí)行異常則觸發(fā) onError() 。

toObservable():延時(shí)執(zhí)行,即事件subscribe()完成事件注冊(cè)后執(zhí)行 run()/construct() 。

第一步是事件注冊(cè)前,調(diào)用 toObservable() 就直接返回一個(gè) Observable<String> 對(duì)象,

第二步調(diào)用 subscribe() 完成事件注冊(cè)后自動(dòng)觸發(fā)執(zhí)行 run()/construct()(如果繼承的是HystrixCommand,hystrix將創(chuàng)建新線程非堵塞執(zhí)行 run() ,調(diào)用程序不必等待 run() ;如果繼承的是HystrixObservableCommand,將以調(diào)用程序線程堵塞執(zhí)行 construct(),調(diào)用程序等待construct()執(zhí)行完才能繼續(xù)往下走),如果 run()/construct() 執(zhí)行成功則觸發(fā) onNext() 和 onCompleted() ,如果執(zhí)行異常則觸發(fā) onError() 。

備注:execute()和queue()是HystrixCommand中的方法,observe()和toObservable()是HystrixObservableCommand 中的方法。其中HystrixCommand是用來(lái)獲取一條數(shù)據(jù)的;HystrixObservableCommand是用來(lái)獲取多條數(shù)據(jù)的。從底層實(shí)現(xiàn)來(lái)講,HystrixCommand其實(shí)也是利用Observable實(shí)現(xiàn)的(如果我們看Hystrix的源碼的話(huà),可以發(fā)現(xiàn)里面大量使用了RxJava),雖然HystrixCommand只返回單個(gè)的結(jié)果,但HystrixCommand的queue方法實(shí)際上是調(diào)用了toObservable().toBlocking().toFuture(),而execute方法實(shí)際上是調(diào)用了queue().get()。

l 獲取單個(gè)產(chǎn)品Command

public class GetProductInfoCommand extends HystrixCommand{

private Long productId;

public GetProductInfoCommand(Long productId) {

super(HystrixCommandGroupKey.Factory.asKey("GetProductInfoCommandGroup"));

this.productId=productId;

}

@Override

protected ProductInfo run() throws Exception {

String url = "http://127.0.0.1:8082/getProductInfo?productId="+productId;

String response = HttpClientUtils.sendGetRequest(url);

return JSONObject.parseObject(response,ProductInfo.class);

}

}

//使用

HystrixCommand command = new GetProductInfoCommand(productId);

ProductInfo productInfo=command.execute();

l 獲取產(chǎn)品列表Command

// 獲取產(chǎn)品列表Command

public class GetProductInfosCommand extends HystrixObservableCommand {

private String[] productIds;

public GetProductInfosCommand(String[] productIds) {

super(HystrixCommandGroupKey.Factory.asKey("GetProductInfoGroup"));

this.productIds = productIds;

}

@Override

protected Observable construct() {

return Observable.create(new Observable.OnSubscribe() {

public void call(Subscriber observer) {

try {

for(String productId : productIds) {

String url = "http://127.0.0.1:8082/getProductInfo?productId=" + productId;

String response = HttpClientUtils.sendGetRequest(url);

ProductInfo productInfo = JSONObject.parseObject(response, ProductInfo.class);

observer.onNext(productInfo);

}

observer.onCompleted();

} catch (Exception e) {

observer.onError(e);

}

}

}).subscribeOn(Schedulers.io());

}

}

//使用

HystrixObservableCommand getProductInfosCommand =

new GetProductInfosCommand(productIds.split(","));

Observable observable = getProductInfosCommand.observe();

//observable = getProductInfosCommand.toObservable(); // 還沒(méi)有執(zhí)行

observable.subscribe(new Observer() { // 等到調(diào)用subscribe然后才會(huì)執(zhí)行

public void onCompleted() {

System.out.println("獲取完了所有的商品數(shù)據(jù)");

}

public void onError(Throwable e) {

e.printStackTrace();

}

public void onNext(ProductInfo productInfo) {

System.out.println(productInfo);

}

});

1.3.2 限流(通過(guò)配置)

限流在日常生活中很常見(jiàn),比如節(jié)假日你去一個(gè)旅游景點(diǎn),為了不把景點(diǎn)撐爆,管理部門(mén)通常會(huì)在外面設(shè)置攔截,限制景點(diǎn)的進(jìn)入人數(shù)(等有人出來(lái)之后,再放新的人進(jìn)去)。對(duì)應(yīng)到計(jì)算機(jī)中,比如要搞活動(dòng)、秒殺等,通常都會(huì)限流。在Hystrix中:

l 如果是線程隔離,可以通過(guò)線程數(shù)+隊(duì)列大小限制。參數(shù)如下:

hystrix.threadpool.default.coreSize

hystrix.threadpool.default.maxQueueSize

hystrix.threadpool.default.queueSizeRejectionThreshold

hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds

l 如果是信號(hào)量隔離,可以設(shè)置最大并發(fā)請(qǐng)求數(shù)。參數(shù)如下:

hystrix.command.default.execution.isolation.semaphore.maxConcurrentRequests

1.3.3 熔斷(CircuitBreaker)

熔斷器的原理很簡(jiǎn)單,如同電力過(guò)載保護(hù)器。它可以實(shí)現(xiàn)快速失敗,如果它在一段時(shí)間內(nèi)偵測(cè)到許多類(lèi)似的錯(cuò)誤,會(huì)強(qiáng)迫其以后的多個(gè)調(diào)用快速失敗,不再訪問(wèn)遠(yuǎn)程服務(wù)器,從而防止應(yīng)用程序不斷地嘗試執(zhí)行可能會(huì)失敗的操作,使得應(yīng)用程序繼續(xù)執(zhí)行而不用等待修正錯(cuò)誤,或者浪費(fèi)CPU時(shí)間去等到長(zhǎng)時(shí)間的超時(shí)產(chǎn)生。熔斷器也可以使應(yīng)用程序能夠診斷錯(cuò)誤是否已經(jīng)修正,如果已經(jīng)修正,應(yīng)用程序會(huì)再次嘗試調(diào)用操作。

熔斷器模式就像是那些容易導(dǎo)致錯(cuò)誤的操作的一種代理。這種代理能夠記錄最近調(diào)用發(fā)生錯(cuò)誤的次數(shù),然后決定使用允許操作繼續(xù),或者立即返回錯(cuò)誤。

熔斷器開(kāi)關(guān)相互轉(zhuǎn)換的邏輯如下圖:

熔斷器就是保護(hù)服務(wù)高可用的最后一道防線。

當(dāng)Hystrix Command請(qǐng)求后端服務(wù)時(shí),在一定時(shí)間內(nèi)(metrics.rollingStats.timeInMilliseconds,默認(rèn)10s),請(qǐng)求次數(shù)超過(guò)了最低要求(circuitBreaker.requestVolumeThreshold,默認(rèn)20次),并且其失敗數(shù)量超過(guò)一定比例(circuitBreaker.errorThresholdPercentage,默認(rèn)50%),斷路器會(huì)切換到開(kāi)路狀態(tài)(Open). 這時(shí)所有請(qǐng)求會(huì)直接失敗而不會(huì)發(fā)送到后端服務(wù). 斷路器保持在開(kāi)路狀態(tài)一段時(shí)間后(circuitBreaker.sleepWindowInMilliseconds,默認(rèn)5秒), 自動(dòng)切換到半開(kāi)路狀態(tài)(HALF-OPEN). 這時(shí)會(huì)判斷下一次請(qǐng)求的返回情況, 如果請(qǐng)求成功, 斷路器切回閉路狀態(tài)(CLOSED), 否則重新切換到開(kāi)路狀態(tài)(OPEN). Hystrix的斷路器就像我們家庭電路中的保險(xiǎn)絲, 一旦后端服務(wù)不可用, 斷路器會(huì)直接切斷請(qǐng)求鏈, 避免發(fā)送大量無(wú)效請(qǐng)求影響系統(tǒng)吞吐量, 并且斷路器有自我檢測(cè)并恢復(fù)的能力.

1.3.4 降級(jí)(Fallback)

Fallback相當(dāng)于是降級(jí)操作。所謂降級(jí),就是指在Hystrix執(zhí)行非核心鏈路功能失敗的情況下,該如何處理,比如返回默認(rèn)值或者從緩存中取值

觸發(fā)降級(jí)的情況

1、hystrix調(diào)用各種接口,或者訪問(wèn)外部依賴(lài)(如mysql、redis等等)時(shí),執(zhí)行方法中拋出了異常。

2、對(duì)每個(gè)外部依賴(lài),無(wú)論是服務(wù)接口,中間件,資源隔離,對(duì)外部依賴(lài)只能用一定量的資源去訪問(wèn),線程池/信號(hào)量,如果資源池已滿(mǎn),則后續(xù)的請(qǐng)求將會(huì)被 reject,即進(jìn)行限流。

3、訪問(wèn)外部依賴(lài)的時(shí)候,訪問(wèn)時(shí)間過(guò)長(zhǎng),可能就會(huì)導(dǎo)致超時(shí),報(bào)一個(gè)TimeoutException異常,即Timeout機(jī)制。

上述三種情況,都是常見(jiàn)的異常情況,對(duì)外部依賴(lài)的東西訪問(wèn)的時(shí)候出現(xiàn)了異常,發(fā)送異常事件到斷路器中去進(jìn)行統(tǒng)計(jì)。

4、如果斷路器發(fā)現(xiàn)異常事件的占比達(dá)到了一定的比例,直接開(kāi)啟斷路器。

上述四種情況,都會(huì)去調(diào)用fallback降級(jí)機(jī)制。

如果要實(shí)現(xiàn)回退或者降級(jí)處理,代碼上需要實(shí)現(xiàn)HystrixCommand.getFallback()方法或者是HystrixObservableCommand. HystrixObservableCommand()。

1.3.5 Hystrix請(qǐng)求緩存(request cache)

Hystrix支持將一個(gè)請(qǐng)求結(jié)果緩存起來(lái),在同一個(gè)請(qǐng)求上下文中,具有相同key的請(qǐng)求將直接從緩存中取出結(jié)果,很適合查詢(xún)類(lèi)的接口,可以使用緩存進(jìn)行優(yōu)化,減少請(qǐng)求開(kāi)銷(xiāo),從而跳過(guò)真實(shí)服務(wù)的訪問(wèn)請(qǐng)求。

Hystrix請(qǐng)求結(jié)果緩存的作用:

1、在同一個(gè)請(qǐng)求上下文中,可以減少使用相同參數(shù)請(qǐng)求原始服務(wù)的開(kāi)銷(xiāo)。

3、請(qǐng)求緩存在 run() 和 construct() 執(zhí)行之前生效,所以可以有效減少不必要的線程開(kāi)銷(xiāo)。

要使用Hystrix cache功能:

1、需要構(gòu)建 RequestContext ,可以在攔截器中使用 HystrixRequestContext.initializeContext() 和 HystrixRequestContext.shutdown() 來(lái)初始化 RequestContext 和 關(guān)閉RequestContext資源。

2、需要重寫(xiě) HystrixCommand 或 HystrixObservableCommand 中的 getCacheKey() 方法,指定緩存的 key,開(kāi)啟緩存配置。

l 配置HystrixRequestContextServletFilter

@WebFilter(filterName = "hystrixRequestContextServletFilter",urlPatterns = "/*",asyncSupported = true)

public class HystrixRequestContextServletFilter implements Filter {

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {

HystrixRequestContext context = HystrixRequestContext.initializeContext();

try {

chain.doFilter(request, response);

} finally {

context.shutdown();

}

}

@Override

public void init(FilterConfig filterConfig) throws ServletException {

}

@Override

public void destroy() {

}

}

l 開(kāi)啟緩存功能:繼承HystrixCommand或HystrixObservableCommand,覆蓋getCacheKey()方法,指定緩存的key,開(kāi)啟緩存配置。

private static final HystrixCommandKey COMMAND_KEY= HystrixCommandKey.Factory.asKey("GetProductInfoCommand");

@Override

protected String getCacheKey() {

return "product_info_"+productId;

}

public static void flushCache(Long productId){

HystrixRequestCache.getInstance(COMMAND_KEY, HystrixConcurrencyStrategyDefault.getInstance()).clear("product_info_"+productId);

}

1.3.6 Hystrix請(qǐng)求合并(request collapser)

1.4 Feign使用Hystrix

1.4.1 服務(wù)端--添加超時(shí)及異常API

@RestController

public class HelloController {

private static Logger LOGGER = LoggerFactory.getLogger(HelloController.class);

@RequestMapping("/hello")

public @ResponseBody User hello(@RequestParam(value = "id") int id) {

LOGGER.info("invoking hello endpoint");

return new User(id, "hello consul from provider 1");

}

@RequestMapping("/timeout")

public String timeout() throws InterruptedException {

LOGGER.info("invoking timeout endpoint");

Thread.sleep(10000L);

return "timeout from provider 1";

}

@RequestMapping("/exception")

public String exception() {

LOGGER.info("invoking exception endpoint");

if (System.currentTimeMillis() % 2 == 0) {

throw new RuntimeException("random exception from provider 1");

}

return "exception from provider 1";

}

}

1.4.2 客戶(hù)端--啟動(dòng)設(shè)置

@EnableFeignClients

@EnableHystrixDashboard

@EnableCircuitBreaker

@SpringBootApplication

public class SpringBootConsulApplication {

public static void main(String[] args) {

SpringApplication.run(SpringBootConsulApplication.class, args);

}

}

1.4.3 客戶(hù)端--Yaml配置

server:

port: 8504

spring:

application:

name: service-consumer

cloud:

consul:

discovery:

register: false

host: 192.168.56.10

port: 8500

feign:

httpclient:

connection-timeout: 5000

connection-timer-repeat: 5000

enabled: true

max-connections: 200

max-connections-per-route: 50

hystrix:

enabled: true

ribbon:

# 暫不開(kāi)啟熔斷機(jī)制

hystrix:

enabled: false

# 配置ribbon默認(rèn)的超時(shí)時(shí)間

ConnectTimeout: 5000

ReadTimeout: 5000

# 是否開(kāi)啟重試

OkToRetryOnAllOperations: true

# 每個(gè)實(shí)例重試次數(shù)

MaxAutoRetries: 2

# 重試的時(shí)候?qū)嵗袚Q次數(shù)

MaxAutoRetriesNextServer: 3

## hystrix相關(guān)配置

## hystrix默認(rèn)會(huì)讀取classpath下的config.properties文件,application會(huì)覆蓋config.properties中的屬性

hystrix:

threadpool:

# 指定服務(wù)的配置

user-service:

coreSize: 20

maxQueueSize: 200

queueSizeRejectionThreshold: 3

# userThreadPool是UserTimeOutCommand中配置的threadPoolKey

userThreadPool:

coreSize: 20

maxQueueSize: 20

queueSizeRejectionThreshold: 3

# 這是默認(rèn)的配置

default:

coreSize: 10

maxQueueSize: 200

queueSizeRejectionThreshold: 2

command:

# 指定feign客戶(hù)端中具體的方法

HelloRemoteService#timeout():

execution:

isolation:

thread:

timeoutInMilliseconds: 5000

userCommandKey:

execution:

isolation:

thread:

timeoutInMilliseconds: 1000

# 這是默認(rèn)的配置

default:

execution:

timeout:

enabled: true

isolation:

strategy: THREAD

thread:

timeoutInMilliseconds: 15000

interruptOnTimeout: true

interruptOnFutureCancel: false

semaphore:

maxConcurrentRequests: 2

fallback:

enabled: true

isolation:

semaphore:

maxConcurrentRequests: 10

circuitBreaker:

enabled: true

forceOpen: false

forceClosed: false

requestVolumeThreshold: 4

errorThresholdPercentage: 50

sleepWindowInMilliseconds: 10000

metrics:

rollingStats:

timeInMilliseconds: 5000

numBuckets: 10

rollingPercentile:

enabled: true

timeInMilliseconds: 60000

numBuckets: 6

bucketSize: 100

healthSnapshot:

intervalInMilliseconds: 500

1.4.4 客戶(hù)--消費(fèi)接口設(shè)置

@Component

@FeignClient(name= "service-provider", fallback = HelloRemoteFallbackService.class)

public interface HelloRemoteService {

@RequestMapping(value = "/hello")

public User hello(@RequestParam(value = "id") int id);

@RequestMapping(value = "/timeout", method = RequestMethod.GET)

public String timeout();

@RequestMapping(value = "/exception", method = RequestMethod.GET)

public String exception();

}

1.4.5 客戶(hù)端--Fallback設(shè)置

@Component

public class HelloRemoteFallbackService implements HelloRemoteService {

@Override

public User hello(int id)

{

System.out.println("調(diào)用服務(wù)失敗--hello");

return null;

}

@Override

public String timeout() {

System.out.println("調(diào)用服務(wù)失敗--timeout");

return "timeout 降級(jí)";

}

@Override

public String exception() {

System.out.println("調(diào)用服務(wù)失敗--exception");

return "exception 降級(jí)";

}

}

1.4.6 客戶(hù)端--相關(guān)配置

# 線程池大小

hystrix.threadpool.default.coreSize=1

# 緩沖區(qū)大小, 如果為-1,則不緩沖,直接進(jìn)行降級(jí) fallback

hystrix.threadpool.default.maxQueueSize=200

# 緩沖區(qū)大小超限的閾值,超限就直接降級(jí)

hystrix.threadpool.default.queueSizeRejectionThreshold=2

# 執(zhí)行策略

# 資源隔離模式,默認(rèn)thread。 還有一種叫信號(hào)量

hystrix.command.default.execution.isolation.strategy=THREAD

# 是否打開(kāi)超時(shí)

hystrix.command.default.execution.timeout.enabled=true

# 超時(shí)時(shí)間,默認(rèn)1000毫秒

hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=15000

# 超時(shí)時(shí)中斷線程

hystrix.command.default.execution.isolation.thread.interruptOnTimeout=true

# 取消時(shí)候中斷線程

hystrix.command.default.execution.isolation.thread.interruptOnFutureCancel=false

# 信號(hào)量模式下,最大并發(fā)量

hystrix.command.default.execution.isolation.semaphore.maxConcurrentRequests=2

# 降級(jí)策略

# 是否開(kāi)啟服務(wù)降級(jí)

hystrix.command.default.fallback.enabled=true

# fallback執(zhí)行并發(fā)量

hystrix.command.default.fallback.isolation.semaphore.maxConcurrentRequests=100

# 熔斷策略

# 啟用/禁用熔斷機(jī)制

hystrix.command.default.circuitBreaker.enabled=true

# 強(qiáng)制開(kāi)啟熔斷

hystrix.command.default.circuitBreaker.forceOpen=false

# 強(qiáng)制關(guān)閉熔斷

hystrix.command.default.circuitBreaker.forceClosed=false

# 前提條件,一定時(shí)間內(nèi)發(fā)起一定數(shù)量的請(qǐng)求。 也就是5秒鐘內(nèi)(這個(gè)5秒對(duì)應(yīng)下面的滾動(dòng)窗口長(zhǎng)度)至少請(qǐng)求4次,熔斷器才發(fā)揮起作用。 默認(rèn)20

hystrix.command.default.circuitBreaker.requestVolumeThreshold=4

# 錯(cuò)誤百分比。達(dá)到或超過(guò)這個(gè)百分比,熔斷器打開(kāi)。 比如:5秒內(nèi)有4個(gè)請(qǐng)求,2個(gè)請(qǐng)求超時(shí)或者失敗,就會(huì)自動(dòng)開(kāi)啟熔斷

hystrix.command.default.circuitBreaker.errorThresholdPercentage=50

# 10秒后,進(jìn)入半打開(kāi)狀態(tài)(熔斷開(kāi)啟,間隔一段時(shí)間后,會(huì)讓一部分的命令去請(qǐng)求服務(wù)提供者,如果結(jié)果依舊是失敗,則又會(huì)進(jìn)入熔斷狀態(tài),如果成功,就關(guān)閉熔斷)。 默認(rèn)5秒

hystrix.command.default.circuitBreaker.sleepWindowInMilliseconds=10000

# 度量策略

# 5秒為一次統(tǒng)計(jì)周期,術(shù)語(yǔ)描述:滾動(dòng)窗口的長(zhǎng)度為5秒

hystrix.command.default.metrics.rollingStats.timeInMilliseconds=5000

# 統(tǒng)計(jì)周期內(nèi) 度量桶的數(shù)量,必須被timeInMilliseconds整除。作用:

hystrix.command.default.metrics.rollingStats.numBuckets=10

# 是否收集執(zhí)行時(shí)間,并計(jì)算各個(gè)時(shí)間段的百分比

hystrix.command.default.metrics.rollingPercentile.enabled=true

# 設(shè)置執(zhí)行時(shí)間統(tǒng)計(jì)周期為多久,用來(lái)計(jì)算百分比

hystrix.command.default.metrics.rollingPercentile.timeInMilliseconds=60000

# 執(zhí)行時(shí)間統(tǒng)計(jì)周期內(nèi),度量桶的數(shù)量

hystrix.command.default.metrics.rollingPercentile.numBuckets=6

# 執(zhí)行時(shí)間統(tǒng)計(jì)周期內(nèi),每個(gè)度量桶最多統(tǒng)計(jì)多少條記錄。設(shè)置為50,有100次請(qǐng)求,則只會(huì)統(tǒng)計(jì)最近的10次

hystrix.command.default.metrics.rollingPercentile.bucketSize=100

# 數(shù)據(jù)取樣時(shí)間間隔

hystrix.command.default.metrics.healthSnapshot.intervalInMilliseconds=500

# 設(shè)置是否緩存請(qǐng)求,request-scope內(nèi)緩存

hystrix.command.default.requestCache.enabled=false

# 設(shè)置HystrixCommand執(zhí)行和事件是否打印到HystrixRequestLog中

hystrix.command.default.requestLog.enabled=false

# 限流策略

#如果沒(méi)有定義HystrixThreadPoolKey,HystrixThreadPoolKey會(huì)默認(rèn)定義為HystrixCommandGroupKey的值

hystrix.threadpool.userGroup.coreSize=1

hystrix.threadpool.userGroup.maxQueueSize=-1

hystrix.threadpool.userGroup.queueSizeRejectionThreshold=800

hystrix.threadpool.userThreadPool.coreSize=1

hystrix.threadpool.userThreadPool.maxQueueSize=-1

hystrix.threadpool.userThreadPool.queueSizeRejectionThreshold=800

hystrix.command.userCommandKey.execution.isolation.thread.timeoutInMilliseconds=5000

1.5 設(shè)置TimeOut注意事項(xiàng)

l 如果hystrix.command.default.execution.timeout.enabled為true,則會(huì)有兩個(gè)執(zhí)行方法超時(shí)的配置,一個(gè)就是ribbon的ReadTimeout,一個(gè)就是熔斷器hystrix的timeoutInMilliseconds, 此時(shí)誰(shuí)的值小誰(shuí)生效

l 如果hystrix.command.default.execution.timeout.enabled為false,則熔斷器不進(jìn)行超時(shí)熔斷,而是根據(jù)ribbon的ReadTimeout拋出的異常而熔斷,也就是取決于ribbon

l ribbon的ConnectTimeout,配置的是請(qǐng)求服務(wù)的超時(shí)時(shí)間,除非服務(wù)找不到,或者網(wǎng)絡(luò)原因,這個(gè)時(shí)間才會(huì)生效

l ribbon還有MaxAutoRetries對(duì)當(dāng)前實(shí)例的重試次數(shù),MaxAutoRetriesNextServer對(duì)切換實(shí)例的重試次數(shù), 如果ribbon的ReadTimeout超時(shí),或者ConnectTimeout連接超時(shí),會(huì)進(jìn)行重試操作

l 由于ribbon的重試機(jī)制,通常熔斷的超時(shí)時(shí)間需要配置的比ReadTimeout長(zhǎng),ReadTimeout比ConnectTimeout長(zhǎng),否則還未重試,就熔斷了

l 為了確保重試機(jī)制的正常運(yùn)作,理論上(以實(shí)際情況為準(zhǔn))建議hystrix的超時(shí)時(shí)間為:(1 + MaxAutoRetries + MaxAutoRetriesNextServer) * ReadTimeout

1.6 Hystrix微服務(wù)優(yōu)化實(shí)例

了解了Hystrix的特性和超時(shí)效果,再看看下面這個(gè)圖,服務(wù)A調(diào)用服務(wù)B和服務(wù)C,服務(wù)C沒(méi)有太復(fù)雜的邏輯處理,300毫秒內(nèi)就處理返回了,服務(wù)B邏輯復(fù)雜,Sql語(yǔ)句就長(zhǎng)達(dá)上百行,經(jīng)常要卡個(gè)5,6秒返回,在大量請(qǐng)求調(diào)用到服務(wù)B的時(shí)候,服務(wù)A調(diào)用服務(wù)B的hystrix線程池已經(jīng)不堪重負(fù),全部卡住

這里的話(huà),首先考慮的就是服務(wù)B的優(yōu)化,優(yōu)化SQL,加索引,加緩存, 優(yōu)化流程,同步改異步,總之縮短響應(yīng)時(shí)間

一個(gè)接口,理論的最佳響應(yīng)速度應(yīng)該在200ms以?xún)?nèi),或者慢點(diǎn)的接口就幾百毫秒。

a. 如何設(shè)置Hystrix線程池大小,Hystrix線程池大小默認(rèn)為10

hystrix:

threadpool:

default:

coreSize: 10

每秒請(qǐng)求數(shù) = 1/響應(yīng)時(shí)長(zhǎng)(單位s) * 線程數(shù) = 線程數(shù) / 響應(yīng)時(shí)長(zhǎng)(單位s)

即:線程數(shù) = 每秒請(qǐng)求數(shù) * 響應(yīng)時(shí)長(zhǎng)(單位s) + (緩沖線程數(shù))

比如一臺(tái)服務(wù), 平均每秒大概收到20個(gè)請(qǐng)求,每個(gè)請(qǐng)求平均響應(yīng)時(shí)長(zhǎng)估計(jì)在500ms,

線程數(shù) = 20 * 500 / 1000 = 10

為了應(yīng)對(duì)峰值高并發(fā),加上緩沖線程,比如這里為了好計(jì)算設(shè)為5,就是 10 + 5 = 15個(gè)線程

b. 如何設(shè)置超時(shí)時(shí)間

還拿上面的例子,比如已經(jīng)配置了總線程是15個(gè),每秒大概20個(gè)請(qǐng)求,那么極限情況,每個(gè)線程都飽和工作,也就是每個(gè)線程一秒內(nèi)處理的請(qǐng)求為 20 / 15 = ≈ 1.3個(gè) , 那每個(gè)請(qǐng)求的最大能接受的時(shí)間就是 1000 / 1.3 ≈ 769ms ,往下取小值700ms.

實(shí)際情況中,超時(shí)時(shí)間一般設(shè)為比99.5%平均時(shí)間略高即可,然后再根據(jù)這個(gè)時(shí)間推算線程池大小

打個(gè)廣告,本人博客地址是:風(fēng)吟個(gè)人博客

?著作權(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)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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