前言:目前咱們的項目是微服務架構,基於dubbo框架,服務之間的調用是經過rpc調用的。剛開始沒有任何問題,項目運行健康、良好。但是過了一段時間,線上總有人反應查詢訂單失敗,等過了一段時間才能查到。這是怎麼回事呢?打開後臺的日誌一看出現了一些RpcException和TimeOutException,原來是遠程調用超時了,可能某個服務在請求的高發期訪問數據庫異常,IO阻塞,返回接口異常了。後來這個問題愈來愈頻繁,如何解決這個棘手的問題呢?前端
本篇博客的目錄java
一:Hystrix是什麼?spring
1.1:基本解釋數據庫
Hystrix最開始由Netflix(看過美劇的都知道,它是一個美劇影視製做的巨頭公司)開源的,後來由Spring Cloud Hystrix基於這款框架實現了斷路器、線程隔離等一系列服務保護功能,該框架的目標在於經過控制訪問遠程系統、服務和第三方庫的節點,從而延遲和故障提供更強大的容錯能力。hystrix具有服務降級、服務熔斷、線程和信號隔離、請求緩存、請求合併以及服務監控等強大功能。起到了微服務的保護機制,防止某個單元出現故障.從而引發依賴關係引起故障的蔓延,最終致使整個系統的癱瘓。緩存
1.2:斷路器的概念springboot
斷路器自己是一個開關裝置,用在電路上保護線路過載,當線路中有電器發生短路的時候。「斷路器」可以及時切斷故障,防止發生過載、發熱甚至起火等嚴重後果。當分佈式架構中,斷路器模式起到的做用也是相似的。當某個服務發生故障的時候,經過斷路器的故障監控向調用方返回一個錯誤響應,而不是長時間的線程掛機,無限等待。這樣就不會使線程因故障服務被長時間佔用不釋放,避免了故障在分佈式系統中的蔓延。以下圖是現實中的斷路器,它是一個開關裝置:服務器
二:Hystrix解決超時問題架構
2.1:問題 app
假設咱們前端提供了用戶查詢訂單的功能,首先請求映射到OrderController,控制器經過調用服務orderService獲取訂單信息,前端傳過來兩個參數:一個是訂單id,一個是用戶id,orderService須要經過用戶id調取用戶服務來獲取用戶的相關信息返回給訂單服務去組裝信息,假設這裏是經過http請求的,咱們有一個單獨的工程叫作:userService部署在其餘的服務器上。可是這個服務器宕機了,這時候訂單服務調取用戶信息就失敗了,而後查詢訂單整個請求就失敗了!由一個服務的宕機就致使整個查詢都失敗了,牽一髮而動全身。流程見下圖:框架
2.2:使用Hystrix進行服務降級
2.2.1:引入hystrix依賴 這裏引入了spring-cloud-starter-netflix-hystrix,springboot的starter裏面整合了hystrix
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Greenwich.SR1</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>4.5.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
2.2.2:模擬訂單請求
首先經過OrderController映射/order請求,獲取前端傳入的參數orderId和useId,而後調用orderDetailService方法,
@RestController public class OrderController { @Resource private OrderService orderService; /** * 獲取訂單信息 * * @param orderNo * @return */ @PostMapping("/order") public ResultVo<OrderDetail> getOrderInfo(@RequestParam("orderId") Long orderNo, @RequestParam("userId") Long userId) { OrderDetail orderDetail = orderService.orderDetailService(orderNo, userId); ResultVo resultVo = new ResultVo<>(); resultVo.setCode(100); resultVo.setMessage("請求成功"); resultVo.setData(orderDetail); return resultVo; } }
2.2.3:訂單服務調取其餘服務
這裏引入了RestTemplate,它是一個spring封裝的http映射請求工具類,而後經過http請求訪問url = "http://192.168.80.153:8070/user/getUser"獲取用戶名,將值給訂單對象。不過假如在這其中發生了調用異常,請求用戶服務異常的話,那麼返回給前端就是一串空的訂單信息,致使用戶看到的訂單爲空。在使用hystrix以後,能夠用@HystrixCommand(fallbackMethod = "orderFallBack")註解,在fallbackMethod中指定回退的方法,這裏必須注意在@HystrixCommand上的方法其指定的回調方法必須和原方法的參數保持一致,這裏包括參數類型、參數個數、參數順序。咱們在回調用法中模擬去查詢緩存數據,返回給訂單。有人又要問了,若是查詢緩存服務器再異常呢?不排除這種可能性。若是是這樣的話,依然可使用@HystrixCommand註解在回調方法中,再指定其餘的回調方法:
@Service public class OrderService { @Autowired private RestTemplate restTemplate; /** * 根據訂單id獲取訂單詳情 * * @param orderId * @param userId * @return */ @HystrixCommand(fallbackMethod = "orderFallBack") public OrderDetail orderDetailService(Long orderId, Long userId) { if (Objects.isNull(orderId)) { return null; } OrderDetail orderDetail = OrderDBSource.getOrderDB().get(orderId); //調用user服務 final String url = "http://192.168.80.153:8070/user/getUser"; String userName = ""; ResponseEntity<String> responseEntity = restTemplate.postForEntity(url, userId, String.class); String returnContent = responseEntity.getBody(); if (Objects.nonNull(responseEntity) && StrUtil.isNotEmpty(returnContent)) { userName = returnContent; } if (ObjectUtil.isNotNull(orderDetail)){ orderDetail.setUserName(userName); } return orderDetail; } /** * 異常調用的回調方法 * * @return */ public OrderDetail orderFallBack(Long orderId, Long userId) { OrderDetail orderDetail = OrderDBSource.getOrderCache().get(orderId); final String unknown = "未知用戶"; orderDetail.setUserName(unknown); return orderDetail; } }
2.3.4:模擬測試
爲了方便測試,首先咱們將請求服務暫時先註釋,而後用postman測試看正常的返回應該是這樣的,這裏使用了備註爲數據庫獲取的訂單,代表它沒有走回調方法,由於這裏沒有訪問用戶url獲取用戶信息,程序能夠正常訪問。我再放開
加上獲取用戶服務的連接,實際上用戶服務是沒法訪問到的,訪問的話就會超時,超時會被hystrix捕捉到,而後走fallBack指定的方法,咱們來測試一下,能夠看到實際上走的是緩存中查詢到的訂單,能夠看到用戶服務已經成功的降級了,降級後的訂單信息雖然是緩存獲取到的,可能會存在延時等問題(固然只要維護好緩存就能夠避免這個問題)。可是比沒有任何數據帶來的用戶一點會更好!
三:Hystrix的流程
Hystrix實際上的工做原理是這樣的:經過command來解耦請求與返回操做,在具體的實例中就是,Hystrix會對依賴的服務進行觀察,經過command.toObservable調用返回一個觀察的對象,同時發起一個事件,而後用Subscriber對接受到的事件進行處理。在command命令發出請求後,它經過一系列的判斷,順序依次是緩存是否命中、斷路器是否打開、線程池是否佔滿,而後它纔會開始對咱們編寫的代碼進行實際的請求依賴服務的處理,也就是Hystrix.run方法,若是在這其中任一節點出現錯誤或者拋出異常,它都會返回到fallback方法進行服務降級處理,當降級處理完成以後,它會將結果返回給,際的調用者,通過一系列流程處理的,它的具體工做流程以下:
四:總結
本篇博客講述了Hystrix是什麼?而後解釋了Hystrix如何進行服務降級處理以及簡單的處理流程,講到的內容是最爲經常使用的功能,還有一些關於Hystrix的緩存、線程池的隔離技術等因爲篇幅的緣由,沒有詳細的講解到,不過做爲一篇入門級的Hystrix教程博客是基本夠的。在實際的開發中,如何保持服務的健壯性、服務的可用性、儘可能的減小bug,提高用戶體驗都是咱們開發者的使命,這條優化和提高之路永遠沒有盡頭,go ahead!
參考資料《spring cloud微服務實戰》
*若是你在本篇博客中,有任何疑問,均可以添加java學習交流羣:618626589