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í)間推算線程池大小