<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          《吃透微服務》 - 服務容錯之Sentinel

          共 16397字,需瀏覽 33分鐘

           ·

          2021-06-29 07:27

          大家好,我是小菜。一個希望能夠成為 吹著牛X談架構 的男人!如果你也想成為我想成為的人,不然點個關注做個伴,讓成長道路不再孤單!

          本文主要介紹 SpringCloud中Sentinel

          如有需要,可以參考

          如有幫助,不忘 點贊 ?

          微信公眾號已開啟,小菜良記,沒關注的同學們記得關注哦!

          上篇我們已經(jīng)了解到微服務中重要的組件之一 --- 服務網(wǎng)關Gateway 。我們在取精排糠的同時,不可否認微服務給我們帶來的好處。其中承載高并發(fā)的好處更是讓各大公司趨之若鶩!

          《吃透微服務》 - 服務網(wǎng)關之Gateway

          但是想要接收高并發(fā),自然要接收它帶來的一系列問題。在微服務架構中,我們將業(yè)務拆分成了一個個服務,這樣的好處之一便是分擔壓力,一個服務頂不住了,下一個服務頂上去。但是服務與服務之間可以相互調(diào)用,由于網(wǎng)絡原因或自身的原因,服務并不能保證百分百可用,也就是各大公司現(xiàn)在追尋的幾個9(99.99%,99.999%)可用!如果單個服務出現(xiàn)問題,調(diào)用這個服務就會出現(xiàn)網(wǎng)絡延遲,此時如果正好有大量的網(wǎng)絡涌入,勢必會形成任務堆積,導致服務癱瘓!

          空口無憑,小菜給你整個示例:

          OrderController

          bd4eeab6d5d5e41ad0f1096ac93aa201.webp

          這里我們通過接口模擬了下單的場景,其中通過線程休眠的方式模擬了網(wǎng)絡延遲的場景。接下來我們還需要改一下 tomcat 中并發(fā)的線程數(shù)

          applicatio.yaml

          server:
          ??tomcat:
          ????max-threads:?10??#?默認的并發(fā)數(shù)為10

          當這一切準備就緒好,我們這個時候還需要壓測工具 Jmeter 的幫助(不會操作的同學具體看以下使用)

          1. 首先打開Jmeter軟件,選擇新建線程組
          35725f77c9c86c29739ed1aad5a644b4.webp
          1. 設置請求線程數(shù)
          a7e00955312f1663727372284f8a1c93.webp
          1. 設置HTTP請求取樣器
          5eaa5a6315250fbe240dedcbbb4cdf2b.webp
          1. 設置請求的接口
          024306647958fa9294e67409cab5cd0e.webp

          完成上面步驟就可以點擊開始了。不過測試之前我們確保兩個API都是可以訪問的:

          1667aca680e7c5e71f0d3b25b7d9a1a1.webpimage-20210612220817883fe98be6952f1b7166720369098900eae.webp

          然后開始壓力測試,當大量請求發(fā)送到創(chuàng)建訂單的接口時,我們這時候通過網(wǎng)頁訪問 detail API 發(fā)現(xiàn)請求一直在阻塞,過一會才聯(lián)通!

          a6345dd96b60588264e47ea586509f15.webp

          這無疑是一個開發(fā)炸彈,而這便是高并發(fā)帶來的問題。看到這里,恭喜你成功見證了一場服務雪崩的問題。那不妨帶著這份興趣繼續(xù)往下看,會給你驚喜的。

          Sentinel

          一、服務雪崩

          我們開頭直接用服務雪崩勾引你,不知道你心動了沒有,如果不想你的項目在線上環(huán)境面臨同樣的問題,趕緊為項目搭線起來,不經(jīng)意的舉動往往能讓你升職加薪!

          在分布式系統(tǒng)中,由于網(wǎng)絡原因或自身的原因。服務一般無法保證 100% 可用,如果一個服務出現(xiàn)了問題,調(diào)用這個服務就會出現(xiàn)線程阻塞的情況,此時若有大量的請求涌入,就會出現(xiàn)多條線程阻塞等待,進而導致服務癱瘓。而由于服務與服務之間的依賴性,故障會進行傳播,相互影響之下,會對整個微服務系統(tǒng)造成災難性的嚴重后果,這就是服務故障的 “雪崩效應”

          最開始的時候,服務A~C 三個服務其樂融融的相互調(diào)用,響應也很快,為主人工作也很賣力

          0f73ff08417887fe8605d51211ff7d4d.webp

          好景不長,主人火了,并發(fā)量上來了。可能因為服務C還不夠健壯的原因,服務C在某一天宕機了,但是服務B還是依賴服務C,不停的進行服務調(diào)用

          ec40ddb9b853d04e776a65c763ff2a6d.webp

          這個時候可能大家都還沒意識到問題的嚴重性,只是懷疑可能請求太多了,導致服務器變卡了。請求繼續(xù)發(fā)送,服務A這個時候也未知問題,一邊覺得奇怪服務B是不是偷懶了,怎么還不把響應返回給它,一邊繼續(xù)發(fā)送請求。但是這個時候請求都在服務B堆積著,終于有一天服務B也累出問題了

          035e9e3b6a7ee572c1c19a99ed2aff59.webp

          這個時候人們開始抱怨服務A了,卻不知道服務A底層原來還依賴服務B和服務C,而這時服務B和服務C都掛了。服務A這時才想通為什么服務B之前那么久沒返回響應,原來服務B也依賴服務C啊!但是這個時候已經(jīng)晚了,請求不斷接收,不斷堆積,下場都是驚人的相似,也走向了宕機的結果。不過有一點不同的是,服務A宕機后需要承載了用戶各種的罵聲~

          3a61bd2d4605826b27260acae09ac0c0.webp

          可悲的故事警惕了我們,微服務架構之間并沒有那么可靠。有時候真的是說掛就掛,原因各種各樣,不合理的容量設計,高并發(fā)情況下某個方法響應變慢,亦或是某臺機器的資源耗盡。我們?nèi)绻徊扇〈胧荒茏源龜溃粩嗟脑谥貑⒎掌髦醒h(huán)。但是我們可能無法杜絕雪崩的源頭,但是如果我們在問題發(fā)生后做好容錯的準備,保證下一個服務發(fā)生問題,不會影響到其他服務的正常運行,各個服務自掃家門雪,做到獨立,雪落而不雪崩

          二、容錯方案

          想要防止雪崩的擴散,就要做好服務的容錯,容錯說白了就是保護自己不被其他隊友坑,帶進送人頭的行列!那我們有哪些容錯的思路呢?

          1)隔離方案

          它是指將系統(tǒng)按照一定的原則劃分為若干個服務模塊,各個模塊之間相互獨立,無強依賴。當有故障發(fā)生時,能將問題和影響隔離在某個模塊內(nèi)部,而不擴散風險,不涉及其他模塊,不影響整體的系統(tǒng)服務。常見的隔離方式有:線程隔離 和信號量隔離:

          28cf3be93da07c38d3da287e9c8a8f82.webp

          2)超時方案

          在上游服務調(diào)用下游服務的時候,設置一個最大響應時間,如果超過這個時間下游服務還沒響應,那么就斷開連接,釋放掉線程

          fae7cdbbc9ccd0a028be7fb5deb097c8.webp

          3)限流方案

          限流就是限制系統(tǒng)的輸入和輸出流量已達到保護系統(tǒng)的目的。為了保證系統(tǒng)的穩(wěn)固運行,一旦達到需要限制的閾值,就需要限制流量并采用少量措施完成限制流量的目的

          3a896e273906b64ba55d61f5d8e14a02.webp

          限流策略有很多,后期也會考慮出一篇專門將如何進行限流

          4)熔斷方案

          在互聯(lián)網(wǎng)系統(tǒng)中,當下游服務因訪問壓力過大而相應變慢或失敗的時候,上游服務為了保護系統(tǒng)整體的可用性,可以暫時切斷對下游服務的調(diào)用。這種犧牲局部,保全整體的措施就叫做熔斷

          41fa3b5c9396bb3c19f005cfa63b1706.webp

          其中熔斷有分為三種狀態(tài):

          • 熔斷關閉狀態(tài)(Closed)

          服務沒有故障時,熔斷器所處的狀態(tài),對調(diào)用方的調(diào)用不做任何限制

          • 熔斷開啟狀態(tài)(Open)

          后續(xù)對該服務接口的調(diào)用不再經(jīng)過網(wǎng)絡,直接執(zhí)行本地的 fallback 方法

          • 半熔斷狀態(tài)(Half-Open)

          嘗試恢復服務調(diào)用,允許有限的流量調(diào)用該服務,并監(jiān)控成功率。如果成功率達到預期,則說明服務已經(jīng)恢復,進入熔斷關閉狀態(tài);如果成功率依然很低,則重新進入熔斷關閉狀態(tài)

          5)降級方案

          降級其實就是為服務提供一個 B計劃,一旦服務無法正常,就啟用 B計劃

          62197ca3eac26ad0d778c828a6c9d4eb.webp

          方案其實有很多,但是很難說明那種方案是最好的。在開發(fā)者的世界中,沒有最好,只有最適合。那如果自己寫一個容錯方案往往是比較容易出錯的(功力高深者除外),那么為了解決這個問題,我們不妨用第三方已經(jīng)為我實現(xiàn)好的組件!

          三、容錯組件

          1)Hystrix

          Hystrix 是 Netflix 開源的一個延遲和容錯庫,用于隔離訪問遠程系統(tǒng),服務或者第三方庫,防止級聯(lián)失敗,從而提升系統(tǒng)的可用性和容錯性

          2)Resilience4J

          Resilience4J是一款非常輕量,簡單,并且文檔非常清晰,豐富的熔斷工具,這是 Hystrix 官方推薦的替代品。它支持 SpringBoot 1.x/2.x 版本,而且監(jiān)控也支持和 prometheus 等多款主流產(chǎn)品進行整合

          3)Sentinel

          Sentinel 是阿里開源的一款斷路器的實現(xiàn),在阿里巴巴內(nèi)部也已經(jīng)大規(guī)模采用,可以說是非常穩(wěn)定

          不同之處

          容錯組件其實有很多,但各有風騷,下面分別說明這這三種組件的不同之處,如何抉擇,仔細斟酌!

          功能SentinelHystrixresilience4j
          隔離策略信號量隔離(并發(fā)線程數(shù)限流)線程池隔離/信號量隔離信號量隔離
          熔斷降級策略基于響應時間,異常比率,異常數(shù)基于異常比率基于異常比率,響應時間
          實時統(tǒng)計實現(xiàn)時間滑動窗口(LeapArray)時間滑動窗口(基于Rxjava)Ring Bit Buffer
          動態(tài)規(guī)則配置支持多種數(shù)據(jù)源支持多種數(shù)據(jù)源有限支持
          擴展性多個擴展點插件的形式接口的形式
          基于注解的支持支持支持支持
          限流基于 QPS,支持基于調(diào)用關系的限流有限的支持Rate LImiter
          流量整形支持預熱模式,勻速器模式,預熱排隊模式不支持簡單的 Rate Limiter模式
          系統(tǒng)自適應保護支持不支持不支持
          控制臺提供即用的控制臺,可配置規(guī)則,查看秒級監(jiān)控,機器發(fā)現(xiàn)等簡單的監(jiān)控查看不提供控制臺,可對接其他監(jiān)控系統(tǒng)

          之前有說過,一個新秀想讓大伙接受并廣泛使用,肯定得具備良好的特性才能沖出老牌的包圍圈。那么 Sentinel 作為一個微服務中的新秀,只有具備讓人滿意的功能,才能被大伙接受。因此,這篇的主角便是 Sentinel ,不妨深入了解一番!

          四、認識Sentinel

          學會用一個組件之前,我們先需要知道這個組件是什么。

          1)什么是Sentinel

          Sentinel(分布式系統(tǒng)的流量防衛(wèi)兵)是阿里開源的一套用于 服務容錯 的綜合性解決方案。它以流量為切入點,從 流量控制熔斷降級系統(tǒng)負載保護等多個維度來保護服務的穩(wěn)定性。

          2)特性

          • 豐富的應用場景:Sentinel 承接了阿里巴巴近10年的雙十一大促的流量的核心場景。在秒殺、消息削峰填谷,集群流量控制、實時熔斷下游不可用應用等場景游刃有余
          • 完備的實時監(jiān)控: Sentinel 提供了實時的監(jiān)控功能。通過控制臺可以看到接入應用的單臺機器的數(shù)據(jù),甚至500臺以下規(guī)模的集群的匯總情況
          • 廣泛的開源生態(tài): Sentinel 提供開箱即用的與其他開源框架整合模塊,只需要引入相關的依賴進行簡單的配置即可快速接入
          • 完善的 SPI 擴展點: Sentinel 提供簡單易用、完善的 SPI 擴展接口。可以通過擴展接口來快速定制邏輯。例如定制規(guī)則管理,適配動態(tài)數(shù)據(jù)源等

          3)組成部分

          • 核心庫(Java客戶端):不依賴任何框架/庫,能夠運行于所有的Java運行環(huán)境,同時對 Dubbo和SpringCloud 有很好的支持
          • 控制臺(Dashboard):基于SpringBoot開發(fā), 打包后可以直接運行,不需要額外的 Tomcat 等應用容器

          五、上手 Sentinel

          既然 Sentinel 有兩個組成部分,我們分別介紹

          1) 核心庫使用

          最關鍵的一步便是引入依賴

          <dependency>
          ????<groupId>com.alibaba.cloud</groupId>
          ????<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
          </dependency>

          然后編寫一個測試控制器

          @RestController
          @RequestMapping("order")
          public?class?OrderController?{

          ????private?final?static?Logger?LOGGER?=?LoggerFactory.getLogger(OrderController.class);

          ????@GetMapping("/create/{id:.+}")
          ????public?String?createOrder(@PathVariable?String?id)?{
          ????????LOGGER.info("準備下單ID為?[{}]?的商品",?id);
          ????????LOGGER.info("成功下單了ID為?[{}]?的商品",?id);
          ????????return?"success";
          ????}

          ????@GetMapping("/{id:.+}")
          ????public?String?detail(@PathVariable?String?id)?{
          ????????return?StringUtils.join("獲取到了ID為",?id,?"的商品");
          ????}
          }

          2)控制臺使用

          Sentinel 具備完善的控制臺, 其實就抓住了國人開發(fā)的命點。很多人看到有控制臺的使用,毫不猶豫的選擇了它!

          • 首先我們需要下載控制臺的 Jar 包啟動運行,下載地址

          • 下載結束進入到下載目錄中通過以下命令啟動,然后訪問 localhost:8080,即可看到頁面

          java?-Dserver.port=8080?-Dcsp.sentinel.dashboard.server=localhost:8080?-Dproject.name=sentinel-dashboard?-jar?sentinel-dashboard-1.8.1.jar
          5a7785866c315c804d142ca301b8b96e.webp
          • 登錄控制臺(sentinel/sentinel)
          56103751c574541d3cae3481b2f073de.webp

          到這里我們就成功進入 Sentinel的控制臺頁面了,是不是上手十分簡單。但這里控制臺還是空蕩蕩的,那是因為我們項目還沒進行控制臺的相關配置。我們回到 store-order ?訂單服務中,在 application.yaml 中進行配置:

          61ec13a103cd60d594dcbac883cf148f.webp

          sertinel.transport.port為隨意端口號,用來跟控制臺交流的端口;sentinel.transport.dashboard ?為控制臺的訪問地址

          配置完成后,我們便可以啟動 store-order 服務,此時再看控制臺可以發(fā)現(xiàn)已經(jīng)有了 store-order 這個服務了

          3d9fb68006b74aa5cb0466db9b33c0e9.webp

          可能有些小伙伴會覺得奇怪,上面說到用來跟控制臺交流的端口是干嘛用的?有問題,便有進步!這里我們可以順帶了解一下控制臺的使用原理

          d227f868f3439538eb68fde646c036ab.webp

          當 Sentinel應用啟動后,我們需要將我們的微服務程序注冊到控制臺上,也就是在配置文件中指定控制臺的地址,這個是肯定的。但是所謂用來跟控制臺交流的端口,也就是我們每個服務都會通過這個端口跟控制臺傳遞數(shù)據(jù),控制臺也可以通過此端口調(diào)用微服務中的監(jiān)控程序來獲取微服務的各種信息。因此這個端口必不可少,而且每個服務都需要具備獨立的端口號

          3)基本概念

          • 資源

          所謂的資源就是 Sentinel 要保護的東西。資源是 Sentinel 的關鍵概念,它可以使 Java 應用程序中的任何內(nèi)容,可以是一個服務,也可以是一個方法,甚至是一段代碼。

          • 規(guī)則

          所謂的規(guī)則就是用來定義如何進行保護資源的,它是作用于資源之上的,定義以什么樣的方式保護資源,主要包括了流量控制規(guī)則,熔斷降級規(guī)則以及系統(tǒng)保護規(guī)則


          我們來看個簡單的例子,我們設置 order/{id}這個API 的 QPS 為1

          021f79b46d252c5ed623fcb9be14ad5d.webp

          當我們不斷刷新頁面就會發(fā)現(xiàn),已經(jīng)進行了流控

          36c3bc7b826c297bdc00d2b1322c6f5e.webp

          在這里面 order/{id}指的就是 資源,我們設置的QPS閾值就是 規(guī)則

          4)重要功能

          學會用 Sentinel 之前,我們需要清楚 Sentinel 能為我們干點什么

          (1)流量控制

          流量控制在網(wǎng)絡傳輸中是一個常用的概念,它用于調(diào)整網(wǎng)絡包的數(shù)據(jù)。任意時間到來的請求往往是隨機不可控的,而系統(tǒng)的處理能力是有限的。我們需要根據(jù)系統(tǒng)的處理能力對流量進行控制,Sentinel 作為一個調(diào)配器,可以根據(jù)需要把隨機的請求調(diào)整成合適的形狀。

          (2)熔斷降級

          當檢測到調(diào)用鏈路中某個資源出現(xiàn)不穩(wěn)定的表現(xiàn),例如請求響應時間長或者異常比例升高的時候,則對這個資源的調(diào)用進行限制,讓請求快速失敗,避免影響到其他資源而導致級聯(lián)故障

          Sentinel 采用了兩種手段進行解決

          1. 通過并發(fā)線程數(shù)進行限制

          Sentinel 通過限制資源并發(fā)線程的數(shù)量,來減少不穩(wěn)定資源對其他資源的影響。當某個資源出現(xiàn)不穩(wěn)定的情況時,例如響應時間變長,對資源的直接影響就是會造成線程數(shù)的逐步堆積。當 線程數(shù)在特定資源上堆積到一定的數(shù)量之后,對該資源的新請求就會拒絕。堆積的線程完成任務后才會開始繼續(xù)接受請求

          1. 通過響應時間對資源進行降級

          除了對并發(fā)線程數(shù)進行控制之外,Sentinel 還可以通過響應時間來快速降級不穩(wěn)定的資源。當依賴的資源出現(xiàn)響應時間過長后,所有對該資源的訪問都會被直接拒絕,直到過了指定的時間窗口之后才會重新恢復

          這里提一嘴和 Hystrix 的區(qū)別

          兩者的原則實際上都是一致的。都是當一個資源出現(xiàn)問題時,讓其快速失敗,不會波及到其他資源服務。但是在限制的實現(xiàn)上是不一樣的

          • Hystrix 采用的是線程池隔離方式,優(yōu)點是做到了資源之間的隔離,缺點是增加了線程上下文切換的成本
          • Sentinel 采用的是通過并發(fā)線程的數(shù)量和響應時間來對資源做限制的

          個人認為 Sentinel 處理限制的方式更好一些

          (3)系統(tǒng)負載保護

          Sentinel 同時提供系統(tǒng)維度的自適應保護能力。當系統(tǒng)負載較高的時候,如果還持續(xù)讓請求進入可能會導致系統(tǒng)崩潰,無法響應。在集群環(huán)境下,會把本應這臺機器承載的流量轉發(fā)到其他機器上去。如果這個時候其他的機器也處在一個崩潰的邊緣狀態(tài),Sentinel 提供了對應的保護機制,讓系統(tǒng)的入口流量和負載達到一個平衡,保證系統(tǒng)在能力方位之內(nèi)處理最多的請求。

          5)流控規(guī)則

          流控規(guī)則,在我們上面說明 Sentinel 的基本概念時簡單演示了一下。流量控制就是用來監(jiān)控應用流量的 QPS(每秒查詢率)或并發(fā)線程數(shù)等指標,當達到指定的閾值時對流量進行控制,以避免被瞬時的流量高峰沖垮,從而保障應用的高可用性。

          簡單配置

          簇點鏈路 ---> 選擇對應資源 ---> 添加流控

          391183f8b0b26681ca402152f1d60864.webp
          • 資源名:唯一名稱,默認就是請求路徑,支持自定義

          • 針對來源: 指定對哪個微服務進行限流,默認為 default(不區(qū)分來源,全部限制)

          • 閾值類型/單機閾值

          1. QPS (每秒請求數(shù)):當調(diào)用該接口的QPS達到閾值的時候進行限流
          2. 線程數(shù):當調(diào)用該接口的線程數(shù)達到閾值的時候進行限流

          是否集群: 這里暫時不演示集群

          高級配置

          我們點開 高級選項 可以看到多出了兩個額外功能

          a1dd3b0a069eb856a7b44cd8fc40e026.webp

          其中 流控模式 分為 三種

          • 直接(默認):接口達到限流條件時,開啟限流

          • 關聯(lián):當關聯(lián)的資源達到限流條件時,開啟限流(適合做應用讓步)

          • 鏈路: 當從某個接口過來的資源達到限流條件時,開啟限流

          1、關聯(lián)流控

          直接流控 的方式我們在上面已經(jīng)演示過了,我們這里直接說 關聯(lián)流控 如何使用

          89919c025f6abbeddc2d7b33bd2662fa.webp

          使用方式也很簡單,只要添加相關聯(lián)的資源即可。只要關聯(lián)資源 /order/create/{id}的 QPS 每秒超過 1。那么 /order/{id} 就會觸發(fā)流控。這就是 你沖動,我買單

          設置完后,我們需要請我們的老幫手 Jmeter 幫忙測試一下:

          ff8203e78058486839cbd140ea731f45.webp

          這個時候 /order/create/{id} 的QPS已經(jīng)遠遠超過1了,然后我們再試著訪問 /order/{id},發(fā)現(xiàn)已經(jīng)被限流了!

          dba8d33bc969e29456f8095f6e92ed6f.webp

          2、鏈路流控

          鏈路流控模式指的是:當從某個接口過來的資源達到限流條件的時候,開啟限流。它的功能有點類似于針對來源配置項,區(qū)別在于:針對來源是針對上級微服務,而鏈路流控是針對上級接口,也就是說它的粒度更細

          該模式使用麻煩一些,我們需要改造下代碼:

          OrderService

          6ec2927eb410460b81edb2c43e72121a.webp

          OrderController

          75a95377f3541f98c9c5f3480a304442.webp

          application.yaml

          bbe4f8eb41181e9366f22c386659f701.webp

          然后自定義 Sentinel 上下文過濾類 FilterContextConfig

          a93dfdd126ea357dbabd0c059029aec6.webp

          接下來我們在 Sentinel 控制臺流控中添加配置:

          bf4f35c0353bd782631766c8850b03d8.webp

          然后我們看測試結果,發(fā)現(xiàn)以/order/_datail02為入口訪問,會進行流控,而/order/_datail01訪問便不會進行流控

          17ea281be76b66cfea6add339bc949e4.webp

          因此我們清楚了鏈路模式的入口資源是針對方法接口的

          6)降級規(guī)則

          降級規(guī)則指的就是當滿足什么條件的時候,對服務進行降級。Sentinel 提供了三個衡量條件:

          • 慢調(diào)用比例

          當資源的平均相應時間超過閾值(單位 ms)之后,資源進入準降級的狀態(tài)。如果接下來1s內(nèi)持續(xù)進入5個請求,它們的RT都持續(xù)超過這個閾值,那么在接下來的時間窗口(單位 s)之內(nèi),就會對這個方法進行降級。

          a553aca894bd2c89af70662464a22b89.webp
          • 異常比例

          當資源的每秒異常總數(shù)/占通過量的比率超過閾值之后,資源就會進入降低狀態(tài),即在接下的時間窗口(單位 s)之內(nèi),對這個方法的調(diào)用都會自動的返回。異常比率的賦值范圍為 [0.0, 1.0]

          b3e76448c0e998a656b00a97f8a3bd37.webp
          • 異常數(shù)

          當資源近1分鐘的異常數(shù)目超過閾值之后就會直接進行降級。但是這里需要注意的是,由于統(tǒng)計時間窗口是分鐘級別的,若時間窗口小于60s,則結束熔斷狀態(tài)后仍可能再進入熔斷狀態(tài)

          5ec54681b4c0275d7cf4be51fc81533d.webp

          7)熱點規(guī)則

          熱點參數(shù)流控規(guī)則是一種更加細粒度的流控規(guī)則,它允許將規(guī)則具體到參數(shù)上。這里我們可以在代碼里面具體看下怎么使用

          @SentinelResource("order")??//?不添加該注解標識,?熱點規(guī)則不生效
          @GetMapping("/_datail03")
          public?String?detail03(String?arg1,?String?arg2)?{
          ????return?StringUtils.join(arg1,?arg2);
          }

          該API接收兩個參數(shù)arg1arg2,這個時候我們對這個資源添加參數(shù)流控

          de46cb1ca657ac8931c1960316f96cd5.webp

          弄完上面配置后,我們就可以在瀏覽器進行測試了

          當參數(shù)為第二個的時候,無論一秒刷新幾次都不會觸發(fā)流控

          1fc28eef8792489c78dd1a33e3d0eae8.webp

          當參數(shù)為第一個的時候,只要QPS超過了1,就會觸發(fā)流控

          83caa165f36aedf5d5ba2c420de9173d.webp

          這個配置也有高級選項,可以更細顆粒的對參數(shù)進行限制,這里就不再演示了。

          a17ba5011b6a8851e46b4e9395f09a6d.webp

          8)系統(tǒng)規(guī)則

          系統(tǒng)保護規(guī)則是從應用級別的入口流量進行控制,從單臺機器總體的 Load、RT、線程數(shù)、入口QPS、CPU 使用率五個維度監(jiān)控應用數(shù)據(jù),讓系統(tǒng)盡可能跑在最大吞吐量的同時保證系統(tǒng)整體的穩(wěn)定性

          787dc7997451fede4806845133c4e722.webpimage-20210613220336221
          • Load: 僅對 Linux/Unix 有效。當系統(tǒng)的 load 超過閾值時,且系統(tǒng)當前的并發(fā)線程數(shù)超過系統(tǒng)容量時才會觸發(fā)系統(tǒng)保護。系統(tǒng)容量是由系統(tǒng)的 maxQPS * minRT 計算而出,設定的參考值可以參考 CPU 核數(shù) * 2.5
          • RT: 當單臺機器上所有入口流量的平均 RT 達到閾值就會觸發(fā)系統(tǒng)保護,單位是毫秒
          • 線程數(shù): 當單臺機器上所有入口流量的并發(fā)線程數(shù)達到閾值是就會觸發(fā)保護
          • 入口QPS: 當單臺機器上所有入口流量的QPS達到閾值就會觸發(fā)系統(tǒng)保護
          • CPU使用率: 當單臺機器上所有入口流量的CPU使用率達到閾值就會觸發(fā)系統(tǒng)保護

          9)授權規(guī)則

          在某些場景下,我們需要根據(jù)調(diào)用來源來判斷該次請求是否允許放行,這個時候我們可以使用Sentinel的來源訪問控制的功能。來源訪問控制根據(jù)資源的請求來源判斷資源是否能夠通過

          55ab35193ace6467192b4777bdbb8a4c.webp
          • 白名單: 只有請求來源位于白名單內(nèi)才能通過
          • 黑名單: 請求來源位于黑名單時不予通過,其余的則放行通過

          那么問題來了,流控應用是啥玩意?要用這個流控應用,我們還需要借助 Sentinel 中的 RequestOriginParser 接口來處理來源。只要 Sentinel 保護的接口資源被訪問,Sentinel 就會調(diào)用 RequestOriginParser 的實現(xiàn)類去解析訪問源

          CustomRequestOriginParser

          public?class?CustomRequestOriginParser?implements?RequestOriginParser?{
          ????@Override
          ????public?String?parseOrigin(HttpServletRequest?httpServletRequest)?{
          ????????return?httpServletRequest.getParameter("api");
          ????}
          }

          然后我們添加授權規(guī)則

          0a251d48e6d24182129a2379c9bb9d27.webp

          該規(guī)則的作用是,只有當請求URL中帶有參數(shù) api=detail03 才能訪問成功,否則失敗。以下便是測試結果

          31480e24ddd8533f21b33848a8185569.webp3721059d7c28a22e39553f0d040b34ec.webp

          六、擴展 Sentinel

          1)@SentinelResource

          這個注解我們上面已經(jīng)用過,不知道小伙伴們有沒有注意到,上面避開沒講就是為了在這詳細的介紹下!

          該注解的作用就是用來定義資源點。當我們定義了資源點之后,就可以通過 Sentinel 控制臺來設置限流和降級策略來對資源點進行保護。同時還可以通過該注解來指定出現(xiàn)異常時候的處理策略。

          我們點進注解可以看到該注解中存在許多屬性

          6124296c062d988b944690e334e05db7.webp
          屬性作用
          value資源點名稱
          blockHandle處理BlockException的函數(shù)名稱,函數(shù)要求:
          1. 必須是 public
          2. 返回類型參數(shù)與原方法要一致
          3. 默認需和原方法在同一個類中。如果希望使用其他類的函數(shù),可以配置 blockHandlerClass,并制定blockHandlerClass 里面的方法
          blackHandlerClass存放 blockHandler 的類,對應的處理函數(shù)必須用 static ?修飾
          fallback用于在拋出異常時候提供 fallback 處理邏輯。fallback 函數(shù)可以針對所有類型的異常(除了exceptionsToIgnore 中排除的異常),函數(shù)要求:
          1. 返回類型與原方法一致
          2. 參數(shù)類型需和原方法匹配
          3. 默認需和原方法在同一個類中。若希望使用其他類的函數(shù),可配置 fallbackClass,并指定對應的方法
          fallbackClass存放 fallback 的類,對應的處理函數(shù)必須用 static ? 修飾
          defaultFallback用于通用的 fallback 邏輯。默認fallback函數(shù)可以針對所有類型的異常進行處理。若同時配置了 fallback 和 defaultFallback,以fallback為準。函數(shù)要求:
          1. 返回類型與原方法一致
          2. 方法參數(shù)列表為空,或者有一個 Throwable 類型的參數(shù)。
          3. 默認需要和原方法在同一個類中。若希望使用其他類的函數(shù),可配置 fallbackClass ,并指定 fallbackClass 里面的方法。
          exceptionsToIgnore指定排除掉哪些異常。排除的異常不會計入異常統(tǒng)計,也不會進入fallback邏輯,而是原樣拋出。
          exceptionsToTrace需要trace的異常

          我們這里簡單使用演示一下

          • 將限流和降級方法定義在原方法同一個類中
          5fa5b1b7cf31143703389906f96bc154.webp
          • 限流和降級方法定義不在原方法同一個類中
          cfc5bc42d135b979cefa5453fa152704.webp

          然后我們做個簡單的流控設置:

          a560dece30cb721cb817adefb46fd080.webp

          訪問結果:

          05372a3cfbf26f33e51f9793119c0f68.webp2e1ca28fc1e86b41fdbcd3ef95a26c78.webp

          這種提示的方式顯然更加友好!

          2)Sentinel 規(guī)則持久化

          已經(jīng)上手嘗試的同學可能會發(fā)現(xiàn)一個問題,當我們的項目重啟,或者 Sentinel 控制臺重啟都會導致配置被清空了!這是因為這些規(guī)則默認是存放在內(nèi)存中,這可是很大的問題!因此規(guī)則持久化是一個必不可少的工作!當然在 Sentinel 也已經(jīng)很好的支持了這項功能,處理邏輯如下:

          1edd4f7772865896d0aa4691db3bd3f0.webp

          實話說配置類代碼有點長,這里直接貼代碼了,有需要的小伙伴可以拷過去直接用!

          public?class?FilePersistence?implements?InitFunc?{

          ????@Override
          ????public?void?init()?throws?Exception?{
          ????????String?ruleDir?=?new?File("").getCanonicalPath()?+?"/sentinel-rules";
          ????????String?flowRulePath?=?ruleDir?+?"/flow-rule.json";
          ????????String?degradeRulePath?=?ruleDir?+?"/degrade-rule.json";
          ????????String?systemRulePath?=?ruleDir?+?"/system-rule.json";
          ????????String?authorityRulePath?=?ruleDir?+?"/authority-rule.json";
          ????????String?paramFlowRulePath?=?ruleDir?+?"/param-flow-rule.json";
          ????????this.mkdirIfNotExits(ruleDir);
          ????????this.createFileIfNotExits(flowRulePath);
          ????????this.createFileIfNotExits(degradeRulePath);
          ????????this.createFileIfNotExits(systemRulePath);
          ????????this.createFileIfNotExits(authorityRulePath);
          ????????this.createFileIfNotExits(paramFlowRulePath);
          ????????//?流控規(guī)則sentinel
          ????????ReadableDataSource<String,?List<FlowRule>>?flowRuleRDS?=?new
          ????????????????FileRefreshableDataSource<>(
          ????????????????flowRulePath,
          ????????????????flowRuleListParser
          ????????);
          ????????FlowRuleManager.register2Property(flowRuleRDS.getProperty());
          ????????WritableDataSource<List<FlowRule>>?flowRuleWDS?=?new
          ????????????????FileWritableDataSource<>(
          ????????????????flowRulePath,
          ????????????????this::encodeJson
          ????????);
          ????????WritableDataSourceRegistry.registerFlowDataSource(flowRuleWDS);
          ????????//?降級規(guī)則
          ????????ReadableDataSource<String,?List<DegradeRule>>?degradeRuleRDS?=?new
          ????????????????FileRefreshableDataSource<>(
          ????????????????degradeRulePath,
          ????????????????degradeRuleListParser
          ????????);
          ????????DegradeRuleManager.register2Property(degradeRuleRDS.getProperty());
          ????????WritableDataSource<List<DegradeRule>>?degradeRuleWDS?=?new
          ????????????????FileWritableDataSource<>(
          ????????????????degradeRulePath,
          ????????????????this::encodeJson
          ????????);
          ????????WritableDataSourceRegistry.registerDegradeDataSource(degradeRuleWDS);
          ????????//?系統(tǒng)規(guī)則
          ????????ReadableDataSource<String,?List<SystemRule>>?systemRuleRDS?=?new
          ????????????????FileRefreshableDataSource<>(
          ????????????????systemRulePath,
          ????????????????systemRuleListParser
          ????????);
          ????????SystemRuleManager.register2Property(systemRuleRDS.getProperty());
          ????????WritableDataSource<List<SystemRule>>?systemRuleWDS?=?new
          ????????????????FileWritableDataSource<>(
          ????????????????systemRulePath,
          ????????????????this::encodeJson
          ????????);
          ????????WritableDataSourceRegistry.registerSystemDataSource(systemRuleWDS);
          ????????//?授權規(guī)則
          ????????ReadableDataSource<String,?List<AuthorityRule>>?authorityRuleRDS?=?new
          ????????????????FileRefreshableDataSource<>(
          ????????????????authorityRulePath,
          ????????????????authorityRuleListParser
          ????????);
          ????????AuthorityRuleManager.register2Property(authorityRuleRDS.getProperty());
          ????????WritableDataSource<List<AuthorityRule>>?authorityRuleWDS?=?new
          ????????????????FileWritableDataSource<>(
          ????????????????authorityRulePath,
          ????????????????this::encodeJson
          ????????);
          ????????WritableDataSourceRegistry.registerAuthorityDataSource(authorityRuleWDS);
          //?熱點參數(shù)規(guī)則
          ????????ReadableDataSource<String,?List<ParamFlowRule>>?paramFlowRuleRDS?=?new
          ????????????????FileRefreshableDataSource<>(
          ????????????????paramFlowRulePath,
          ????????????????paramFlowRuleListParser
          ????????);
          ????????ParamFlowRuleManager.register2Property(paramFlowRuleRDS.getProperty());
          ????????WritableDataSource<List<ParamFlowRule>>?paramFlowRuleWDS?=?new
          ????????????????FileWritableDataSource<>(
          ????????????????paramFlowRulePath,
          ????????????????this::encodeJson
          ????????);
          ????????ModifyParamFlowRulesCommandHandler.setWritableDataSource(paramFlowRuleWDS);
          ????}

          ????private?final?Converter<String,?List<FlowRule>>?flowRuleListParser?=?source?->
          ????????????JSON.parseObject(
          ????????????????????source,
          ????????????????????new?TypeReference<List<FlowRule>>()?{
          ????????????????????}
          ????????????);

          ????private?final?Converter<String,?List<DegradeRule>>?degradeRuleListParser?=?source
          ????????????->?JSON.parseObject(
          ????????????source,
          ????????????new?TypeReference<List<DegradeRule>>()?{
          ????????????}
          ????);

          ????private?final?Converter<String,?List<SystemRule>>?systemRuleListParser?=?source?->
          ????????????JSON.parseObject(
          ????????????????????source,
          ????????????????????new?TypeReference<List<SystemRule>>()?{
          ????????????????????}
          ????????????);
          ????private?final?Converter<String,?List<AuthorityRule>>?authorityRuleListParser?=
          ????????????source?->?JSON.parseObject(
          ????????????????????source,
          ????????????????????new?TypeReference<List<AuthorityRule>>()?{
          ????????????????????}
          ????????????);
          ????private?final?Converter<String,?List<ParamFlowRule>>?paramFlowRuleListParser?=
          ????????????source?->?JSON.parseObject(
          ????????????????????source,
          ????????????????????new?TypeReference<List<ParamFlowRule>>()?{
          ????????????????????}
          ????????????);

          ????private?void?mkdirIfNotExits(String?filePath)?throws?IOException?{
          ????????File?file?=?new?File(filePath);
          ????????if?(!file.exists())?{
          ????????????file.mkdirs();
          ????????}
          ????}

          ????private?void?createFileIfNotExits(String?filePath)?throws?IOException?{
          ????????File?file?=?new?File(filePath);
          ????????if?(!file.exists())?{
          ????????????file.createNewFile();
          ????????}
          ????}

          ????private?<T>?String?encodeJson(T?t)?{
          ????????return?JSON.toJSONString(t);
          ????}
          }

          然后在 resources 下創(chuàng)建配置目錄 META-INF/services ,然后添加文件com.alibaba.csp.sentinel.init.InitFunc在文件中添加配置類的全路徑

          2cd63093ad9093993198c2e69ab8a87a.webpd062b3c2707452a5b21225afc88b97db.webp

          這樣子我們啟動項目的時候就會生成 Sentinel 的配置文件了

          2447ab4d8ea9e5ee0e9224c10e64b36a.webp

          當我們在控制臺中添加一條流控規(guī)則后,對應的 json 文件就會有對應的配置

          e5fa30ce652c96a122a689a60d4d5868.webpb3e385ee14415ddfb6898a9994d98b1b.webp

          到這里我們就完成了 Sentinel 的持久化功能,到這里我們也完成了對 SpringCloud 中Sentinel 的介紹!

          后面會繼續(xù)整理關于 SpringCloud 組件的文章,敬請關注!

          不要空談,不要貪懶,和小菜一起做個吹著牛X做架構的程序猿吧~點個關注做個伴,讓小菜不再孤單。咱們下文見!

          今天的你多努力一點,明天的你就能少說一句求人的話!

          我是小菜,一個和你一起變強的男人。 ??

          微信公眾號已開啟,小菜良記,沒關注的同學們記得關注哦!


          瀏覽 80
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  色情片在线观看 | 在线成人毛片 | 欧美三级在线 | 大香蕉久操| 蜜桃视频成人网站入口 |