摘要:在艙壁模式中可以隔離每個遠程資源,并分配各自的線程池,使之互不影響。
springcloud 總集:https://www.tapme.top/blog/detail/2019-02-28-11-33
本次用到全部代碼見文章最下方。
一、為什么要有客戶端彈性模式??所有的系統都會遇到故障,分布式系統單點故障概率更高。如何構建應用程序來應對故障,是每個軟件開發人員工作的關鍵部分。但是通常在構建系統時,大多數工程師只考慮到基礎設施或關鍵服務徹底發生故障,使用諸如集群關鍵服務器、服務間的負載均衡以及異地部署等技術。盡管這些方法考慮到組件系統的徹底故障,但他們之解決了構建彈性系統的一小部分問題。當服務崩潰時,很容易檢測到該服務以及失效,因此應用程序可以饒過它。然而,當服務運行緩慢時,檢測到這個服務性能越發低下并繞過它是非常困難的,因為以下幾個原因:
服務的降級可以是以間歇性的故障開始,并形成不可逆轉的勢頭————可能開始只是一小部分服務調用變慢,直到突然間應用程序容器耗盡了線程(所有線程都在等待調用完成)并徹底崩潰。
應用程序通常的設計是處理遠程資源的徹底故障,而不是部分降級————通常,只要服務沒有完全死掉,應用程序將繼續調用這個服務,直到資源耗盡崩潰。
??性能較差的遠程服務會導致很大的潛在問題,它們不僅難以檢測,還會觸發連鎖反應,從而影響整個應用程序生態系統。如果沒有適當的保護措施,一個性能不佳的服務可以迅速拖垮整個應用程序。基于云、基于微服務的應用程序特別容易受到這些類型的終端影響,因為這些應用由大量細粒度的分布式服務組成,這些服務在完成用戶的事務時涉及不同的基礎設施。
二、什么是客戶端彈性模式??客戶端彈性模式是在遠程服務發生錯誤或表現不佳時保護遠程資源(另一個微服務調用或者數據庫查詢)免于崩潰。這些模式的目標是為了能讓客戶端“快速失敗”,不消耗諸如數據庫連接、線程池之類的資源,還可以避免遠程服務的問題向客戶端的消費者進行傳播,引發“雪崩”效應。spring cloud 主要使用的有四種客戶端彈性模式:
客戶端負載均衡(client load balance)模式
??上一篇已經說過,這里不再贅述。
斷路器(circuit breaker)模式
??本模式模仿的是電路中的斷路器。有了軟件斷路器,當遠程服務被調用時,斷路器將監視這個調用,如果調用時間太長,斷路器將介入并中斷調用。此外,如果對某個遠程資源的調用失敗次數達到某個閾值,將會采取快速失敗策略,阻止將來調用失敗的遠程資源。
后備(fallback)模式
??當遠程調用失敗時,將執行替代代碼路徑,并嘗試通過其他方式來處理操作,而不是產生一個異常。也就是為遠程操作提供一個應急措施,而不是簡單的拋出異常。
艙壁(bulkhead)模式
??艙壁模式是建立在造船的基礎概念上。我們都知道一艘船會被劃分為多個水密艙(艙壁),因而即使少數幾個部位被擊穿漏水,整艘船并不會被淹沒。將這個概念帶入到遠程調用中,如果所有調用都使用的是同一個線程池來處理,那么很有可能一個緩慢的遠程調用會拖垮整個應用程序。在艙壁模式中可以隔離每個遠程資源,并分配各自的線程池,使之互不影響。
??下圖展示了這些模式是如何運用到微服務中的:
三、spring cloud 中使用??使用 Netflix 的 Hystrix 庫來實現上述彈性模式。繼續使用上一節的項目,給 licensingservice 服務實現彈性模式。
1、代碼修改 1、依賴引入??首先修改 POM 文件,添加下面兩個依賴:
org.springframework.cloud spring-cloud-starter-hystrix com.netflix.hystrix hystrix-javanica 1.5.9
??然后在啟動類上加入@EnableCircuitBreaker啟用 Hystrix。
2、實現斷路器??首先修改 organizationservice 項目中的 OrganizationController,模擬延遲,每隔兩次讓線程 sleep 2 秒
@RestController public class OrganizationController { private static int count=1; @GetMapping(value = "/organization/{orgId}") public Object getOrganizationInfo(@PathVariable("orgId") String orgId) throws Exception{ if(count%2==0){ TimeUnit.SECONDS.sleep(2); } count++; Mapdata = new HashMap<>(2); data.put("id", orgId); data.put("name", orgId + "公司"); return data; } }
??只需在方法上添加@HystrixCommand,即可實現超時短路。如果 Spring 掃描到該注解注釋的類,它將動態生成一個代理,來包裝這個方法,并通過專門用于處理遠程調用的線程池來管理對該方法的所有調用。
??修改 licensingservice 服務中的 OrganizationByRibbonService,OrganizationFeignClient,給其中的方法加上@HystrixCommand的注解。然后再訪問接口localhost:10011/licensingByRibbon/11313,localhost:10011/licensingByFeign/11313。多次訪問可發現拋出錯誤com.netflix.hystrix.exception.HystrixRuntimeException,斷路器生效,默認情況下操時時間為 1s。
{ "timestamp": 1543823192424, "status": 500, "error": "Internal Server Error", "exception": "com.netflix.hystrix.exception.HystrixRuntimeException", "message": "OrganizationFeignClient#getOrganization(String) timed-out and no fallback available.", "path": "/licensingByFeign/11313/" }
??可通過設置注解參數來修改操時時間。設置超時時間大于 2s 后便不會報操時錯誤。(不知道為什么在 Feign 中設置失敗,ribbon 中正常。)。一般都是將配置寫在配置文件中。
@HystrixCommand(commandProperties = { @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "20000") })3、后備處理
??由于遠程資源的消費者和資源本身之間存在存在一個"中間人",因此開發人員能夠攔截服務故障,并選擇替代方案。在 Hystrix 中進行后備處理,非常容易實現。
在 ribbon 中的實現
??只需在@HystrixCommand注解中加入屬性 fallbackMethod="methodName",那么在執行失敗時,便會執行后備方法。注意防備方法必須和被保護方法在同一個類中,并且方法簽名必須相同。修改 licensingservice 中 service 包下的 OrganizationByRibbonService 類,改為如下:
@Component public class OrganizationByRibbonService { private RestTemplate restTemplate; @Autowired public OrganizationByRibbonService(RestTemplate restTemplate) { this.restTemplate = restTemplate; } @HystrixCommand(commandProperties = { @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "1000") },fallbackMethod = "getOrganizationWithRibbonBackup") public Organization getOrganizationWithRibbon(String id) throws Exception { ResponseEntityresponseEntity = restTemplate.exchange("http://organizationservice/organization/{id}", HttpMethod.GET, null, Organization.class, id); return responseEntity.getBody(); } public Organization getOrganizationWithRibbonBackup(String id)throws Exception{ Organization organization = new Organization(); organization.setId("0"); organization.setName("組織服務調用失敗"); return organization; } }
??啟動應用,多次訪問localhost:10011/licensingByRibbon/11313/,可以發現調用失敗時,會啟用后備方法。
在 feign 中實現
??在 feign 中實現后備模式,需要編寫一個 feign 接口的實現類,然后在 feign 接口中指定該類。以 licensingservice 為例。首先在 client 包中添加一個 OrganizationFeignClientImpl 類,代碼如下:
@Component public class OrganizationFeignClientImpl implements OrganizationFeignClient{ @Override public Organization getOrganization(String orgId) { Organization organization=new Organization(); organization.setId("0"); organization.setName("后備模式返回的數據"); return organization; } }
然后修改 OrganizationFeignClient 接口的注解,將@FeignClient("organizationservice")改為@FeignClient(name="organizationservice",fallback = OrganizationFeignClientImpl.class。
??重啟項目,多次訪問localhost:10011/licensingByFeign/11313/,可發現后備服務起作用了。
??在確認是否要啟用后備服務時,要注意以下兩點:
后備是一種在資源操時或失敗時提供行動方案的機制。如果只是用后備來捕獲操時異常然后只做日志記錄,那只需要 try..catch 即可,捕獲 HystrixRuntimeException 異常。
注意后備方法所執行的操作。如果在后備服務中調用另一個分布式服務,需要注意用@HystrixCommand 方法注解包裝后備方法。
4、實現艙壁模式??在基于微服務的應用程序中,通常需要調用多個微服務來完成特定的任務,在不適用艙壁的模式下,這些調用默認是使用同一批線程來執行調用的,而這些線程是為了處理整個 Java 容器的請求而預留的。因此在存在大量請求的情況下,一個服務出現性能問題會導致 Java 容器內的所有線程被占用,同時阻塞新請求,最終容器徹底崩潰。
??Hystrix 使用線程池來委派所有對遠程服務的調用,默認情況下這個線程池有 10 個工作線程。但是這樣很容易出現一個運行緩慢的服務占用全部的線程,所有 hystrix 提供了一種一種易于使用的機制,在不同的遠程資源調用間創建‘艙壁’,將不同服務的調用隔離到不同的線程池中,使之互不影響。
??要實現隔離的線程池,只需要在@HystrixCommand上加入線程池的注解,這里以 ribbon 為例(Feign 類似)。修改 licensingservice 中 service 包下的 OrganizaitonByRibbonService 類,將getOrganizationWithRibbon方法的注解改為如下:
@HystrixCommand(commandProperties = { @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "1000") }, fallbackMethod = "getOrganizationWithRibbonBackup", threadPoolKey = "licenseByOrgThreadPool", threadPoolProperties = { @HystrixProperty(name = "coreSize", value = "30"), @HystrixProperty(name = "maxQueueSize", value = "10") })
如果將maxQueueSize屬性值設為-1,將使用SynchronousQueue保存所有的傳入請求,同步隊列會強制要求正在處理中的請求數量永遠不能超過線程池的大小。設為大于 1 的值將使用LinkedBlockingQueue。
??注意:示例代碼中都是硬編碼屬性值到 Hystrix 注解中的。在實際應用環境中,一般都是將配置項配置在 Spring Cloud Config 中的,方便統一管理。
本次用到全部代碼:點擊跳轉
本篇原創發布于:FleyX 的個人博客
掃碼關注微信公眾號:FleyX 學習筆記,獲取更多干貨
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/77747.html
摘要:如果由包裝的工作不尊重,那么線程池中的線程將繼續它的工作,盡管客戶機已經收到了。這種行為可能會使線程池飽和,盡管負載正確釋放。 showImg(https://segmentfault.com/img/remote/1460000018779851); 簡介 在分布式環境中,許多服務依賴關系中的一些必然會失敗。Hystrix是一個庫,它通過添加延遲容忍和容錯邏輯來幫助您控制這些分布式服...
摘要:提供給文件上傳微服務用的。注意注解能注冊到服務上,是因為該注解包含了客戶端的注解,該是一個復合注解。地址可以查看該微服務網關代理了多少微服務的。 SpringCloud(第 024 篇)簡單文件上傳微服務,并加入 zuul 微服務后用 zuul 微服務地址采取curl或者頁面點擊實現文件上傳 - 一、大致介紹 1、本章節主要將文件上傳微服務加入到 zuul 服務中去,然后利用 zuul...
摘要:注意注解能注冊到服務上,是因為該注解包含了客戶端的注解,該是一個復合注解。地址可以查看該微服務網關代理了多少微服務的。 SpringCloud(第 020 篇)Zuul 網關模塊添加 listOfServers 屬性,達到客戶端負載均衡的能力 - 一、大致介紹 1、本章節添加另外一個屬性 listOfServers 來給 zuul 賦上異樣的功能色彩,提供負載均衡的能力; 2、而其實說...
摘要:英文意思就是說提供一個回退機制當路由后面的服務發生故障時。注意注解能注冊到服務上,是因為該注解包含了客戶端的注解,該是一個復合注解。地址可以查看該微服務網關代理了多少微服務的。 SpringCloud(第 025 篇)Zuul 路由后面的微服務掛了后,Zuul 提供了一種回退機制來應對熔斷處理 - 一、大致介紹 1、在一些不穩定因素導致路由后面的微服務宕機或者無響應時,zuul 就會累...
摘要:添加訪問遠端用戶微服務類客戶端回退機制類。添加訪問層添加電影微服務啟動類電影微服務接入,添加屬性來觸發請求進行容災降級。注解表示該電影微服務已經接入模塊。 SpringCloud(第 017 篇)電影微服務接入Feign,添加 fallbackFactory 屬性來觸發請求進行容災降級 - 一、大致介紹 1、在一些場景中,簡單的觸發在 FeignClient 加入 Fallback 屬...
閱讀 1265·2021-11-24 09:39
閱讀 1514·2021-09-07 09:59
閱讀 3479·2019-08-30 15:54
閱讀 2474·2019-08-30 11:00
閱讀 2669·2019-08-29 15:06
閱讀 2159·2019-08-26 13:52
閱讀 427·2019-08-26 13:24
閱讀 2488·2019-08-26 12:20