文章首發於公衆號:松花皮蛋的黑板報
做者就任於京東,在穩定性保障、敏捷開發、高級JAVA、微服務架構有深刻的理解nginx
1、容錯的必要性
假設單體應用可用率爲99.99%,即便拆分後每一個微服務的可用率仍是保持在99.99%,整體的可用率仍是降低的。由於凡是依賴均可能會失敗,凡是資源都是有限制的,另外網絡並不可靠。有可能一個很不起眼的微服務模塊高延遲最後致使總體服務不可用編程
2、容錯的基本模塊
一、主動超時,通常設置成2秒或者5秒超時時間
二、服務降級,通常會降級成直接跳轉到靜態CDN託底頁或者提示活動太火爆,以避免開天窗
三、限流,通常使用令牌機制限制最大併發數
四、隔離,對不一樣依賴進行隔離,容器CPU綁核就是一種隔離措施
五、彈性熔斷,錯誤數達到必定閥值後,開始拒絕請求,健康檢查發現恢復後再次接受請求
3、Hystrix主要概念
Hystrix流程後端
想要使用Hystrix,只須要繼承HystrixCommand或者HystrixObservableCommand並重寫業務邏輯方法便可,區別在於HystrixCommand.run()返回一個結果或者異常,HystrixObservableCommand.construct()返回一個Observable對象緩存
編者按:關於反應式編程可參考文章Flux反應式編程結合多線程實現任務編排網絡
Hystrix真正執行命令邏輯是經過execute()、queue()、observe()、toObservable()的其中一種,區別在於execute是同步阻塞的,queue經過myObservable.toList().toBlocking().toFuture()實現異步非阻塞,observe是事件註冊前執行,toObservable是事件註冊後執行,後二者是基於發佈和訂閱響應式的調用 多線程
每一個熔斷器默認維護10個bucket,每秒一個bucket,每一個bucket記錄成功,失敗,超時,拒絕的狀態,默認錯誤超過50%且10秒內超過20個請求才進行中斷攔截。當斷路器打開時,維護一個窗口,每過一個窗口時間,會放過一個請求以探測後端服務健康狀態,若是已經恢復則斷路器會恢復到關閉狀態架構
當斷路器打開、線程池提交任務被拒絕、信號量得到被拒絕、執行異常、執行超時任一狀況發生都會觸發降級fallBack,Hystrix提供兩種fallBack方式,HystrixCommand.getFallback()和HystrixObservableCommand.resumeWithFallback()併發
4、線程和信號量隔離異步
一、線程隔離,針對不一樣的服務依賴建立線程池
二、信號量隔離,本質是一個共享鎖。當信號量中有可用的許可時,線程能獲取該許可(seaphore.acquire()),不然線程必須等待,直到有可用的許可爲止。線程用完必須釋放(seaphore.release())不然其餘線程永久等待ide
5、Hystrix主要配置項
6、使用
一、請求上下文,下面將要提到的請求緩存、請求合併都依賴請求上下文,咱們能夠在攔截器中進行管理
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(); } } }
二、請求緩存,減小相同參數請求後端服務的開銷,須要重寫getCacheKey方法返回緩存key
public class CommandUsingRequestCache extends HystrixCommand<Boolean> { private final int value; protected CommandUsingRequestCache(int value) { super(HystrixCommandGroupKey.Factory.asKey("ExampleGroup")); this.value = value; } @Override protected Boolean run() { return value == 0 || value % 2 == 0; } @Override protected String getCacheKey() { return String.valueOf(value); } }
三、請求合併。請求合併在Nginx靜態資源加載中也很常見,Nginx使用的是nginx-http-concat擴展模塊。可是在Hystric中請求合併會致使延遲增長,因此要求二者啓動執行間隔時長足夠小,減小等待合併的時間,超過10ms間隔不會自動合併
public class CommandCollapserGetValueForKey extends HystrixCollapser<List<String>, String, Integer> { private final Integer key; public CommandCollapserGetValueForKey(Integer key) { this.key = key; } @Override public Integer getRequestArgument() { return key; } @Override protected HystrixCommand<List<String>> createCommand(final Collection<CollapsedRequest<String, Integer>> requests) { return new BatchCommand(requests); } @Override protected void mapResponseToRequests(List<String> batchResponse, Collection<CollapsedRequest<String, Integer>> requests) { int count = 0; for (CollapsedRequest<String, Integer> request : requests) { request.setResponse(batchResponse.get(count++)); } } private static final class BatchCommand extends HystrixCommand<List<String>> { private final Collection<CollapsedRequest<String, Integer>> requests; private BatchCommand(Collection<CollapsedRequest<String, Integer>> requests) { super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("ExampleGroup")) .andCommandKey(HystrixCommandKey.Factory.asKey("GetValueForKey"))); this.requests = requests; } @Override protected List<String> run() { ArrayList<String> response = new ArrayList<String>(); for (CollapsedRequest<String, Integer> request : requests) { // artificial response for each argument received in the batch response.add("ValueForKey: " + request.getArgument()); } return response; } } }
四、快速失敗,不走降級邏輯,直接拋出異常,一般用於非冪等性的寫操做。冪等性是指一次和屢次請求某一個資源應該具備一樣的反作用,好比bool take(ticket_id, account_id, amount)取錢操做,無論任什麼時候候請求失敗或超時,調用方均可以重試,固然把參數ticket_id去掉就是非冪等性的了。注意:在Hystrix能夠輕鬆實現重試,只需降級時判斷isCircuitBreakerOpen斷路器狀態可用而後重試便可,不會使問題雪上加霜
public class CommandThatFailsFast extends HystrixCommand<String> { private final boolean throwException; public CommandThatFailsFast(boolean throwException) { super(HystrixCommandGroupKey.Factory.asKey("ExampleGroup")); this.throwException = throwException; } @Override protected String run() { if (throwException) { throw new RuntimeException("failure from CommandThatFailsFast"); } else { return "success"; } }
文章來源:http://www.liangsonghua.me
做者介紹:京東資深工程師-梁鬆華,長期關注穩定性保障、敏捷開發、JAVA高級、微服務架構