javascript
Spring Cloud Alibaba —— Sentinel 详细使用
導航
- 引言
- 一、Sentinel的兩個基本概念
- 二、流控規則
- 2.1 基本選項
- 2.2 高級選項
- 三、熔斷(降級)規則
- 四、熱點規則
- 五、授權規則(了解)
- 六、系統規則(了解)
- 七、自定義異常返回
- 八、@SentinelResource
- 九、Sentinel 規則持久化(待補充)
- 十、Feign 整合 Sentinel 實現容錯
引言
Sentinel 官方文檔:https://sentinelguard.io/zh-cn/docs/introduction.html
本文承接《Spring Cloud Alibaba —— Sentinel 入門》,繼續深入使用 Sentinel。
本文總結自B站,黑馬程序員視頻《深入學習Java微服務開發(SpringCloud) 》(P21 - P32)。將介紹 Sentinel 的概念和功能、基本配置、流控模式、熔斷降級規則、自定義規則、規則持久化,以及 Feign 整合Sentinel 實現容錯降級邏輯等課題。
一、Sentinel的兩個基本概念
Sentinel 中最基本的概念。資源可以指代任何需要保護的內容,可以是服務、方法,甚至可以是一段代碼。例如,/order/message1 ,這個接口受到了 Sentinel 的流控保護,因此它就是一個資源。
規則就是用來定義如何進行資源保護的。主要分為三種類型:流量控制規則、熔斷降級規則、系統保護規則。
學習 Sentinel 很大程度就是在學習這些規則如何配置。
服務容錯的三個核心思想:不被上游壓垮、不被下游拖垮、保證外界環境(cpu、內存)良好。(這也是服務雪崩的三個主要成因,見《微服務架構 —— 服務雪崩與容錯方案》)
流量控制規則:針對上游請求,由于網絡傳輸是不穩定的,這種不穩定可能導致某時刻涌入大量的請求,但系統的處理能力是有限的,Sentinel 作為調配器,可以根據需要把隨機的請求調整成合適的形狀。所謂合適的形狀,例如漏斗,只允許通過 n 個請求,那么后面的請求就會等待前面的請求通過后才能通過。
熔斷降級:當檢測到調用鏈中出現不穩定的情況,如請求時間長或異常比例升高等。就可以針對頻繁發生故障的節點的調用進行限制,避免產生級聯故障。(兩種手段:限制并發線程數、通過響應時間對資源降級)
系統負載保護:提供了系統緯度的自適應能力。當系統負載高時,如果還持續讓請求進入,可能導致系統崩潰。在集群環境下,可以分散請求到其他機器上,如果其他機器也處于高負載狀態,Sentinel 會采取必要的保護機制,讓系統入口的流量和系統的負載達到一個平衡,保證系統在能力范圍內處理最多請求。
Sentinel 與 Hystrix 比較
兩者的原則都是一致的,都是在發現服務發生故障后,快速失敗,避免產生級聯故障。
區別是,Sentinel 采用并發線程數和響應時間來進行服務熔斷降級,而Hystrix采用線程池隔離的方式,線程池隔離的缺點是增加了線程切換的成本。
二、流控規則
2.1 基本選項
登錄 Sentinel 控制臺,在簇點鏈路中找到需要做流控的接口,這里就以 /order/message1 接口為例:
打開流控規則設置會話框:
其中,針對來源 為 default 代表不限制來源,在微服務中,如果服務 A 和服務 B 都調用 order 服務這個接口,那么可以通過針對服務A或服務B,分別進行流控限制,如果是 default 就不做來源的區分。
QPS 不多解釋,當每秒請求數達到閾值,自然就被限流。這里主要演示下 并發線程數。
并發線程數代表統一時間處理請求的線程數量,如果通過一個客戶端瀏覽器發送請求進行測試,是無法模擬的,因為http一次請求和響應只對應一個線程,手動刷新的方式只能模擬一條線程的情況,這時可以使用 jMeter 工具來進行并發線程數的測試(jMeter 參考 《jMeter 模擬 web 高并發請求》)。
首先,設置 Sentinel 流控規則為 并發線程數 = 2:
然后啟動 jMeter,并添加線程組:
并添加 http 取樣器,請求接口就是 http://localhost:8091/order/message1 ,并點擊發送:
因為我們設置了流控規則為兩個并發線程數,jMeter 的線程數也是2 個,因此就會占用這個線程名額,當我們另一邊手動用瀏覽器請求接口的時候,就會被流控規則限制:
2.2 高級選項
流控模式
-
直接(默認):接口達到限流條件時,開啟限流。
-
關聯:當關聯資源達到限流條件時,開啟限流(適合做應用讓步)。
例如,關聯一個 /order/message2 接口(該資源要確實存在,此處具體代碼略),并設置關聯限流模式:
它表示這個服務節點(單機)的請求每秒不得超過3個,如果關聯資源 /order/message2 的請求超過3,那么就限流掉 /order/message1 的請求,讓資源名指定的資源對關聯資源做出讓步。 -
鏈路:當從某個接口過來的資源達到限流條件時,開啟限流。該方式可以對 服務內部的 service 層方法進行限流,例如兩個controller入口都調用了某 service 方法,我們給該方法標記上@SentinelResource注解,配合鏈路流控模式,就可以針對某個controller進入的請求進行限流操作。它的功能有點類似于“針對來源”的配置,但區別是針對來源是針對上級服務的,而鏈路流控是針對上級接口的,也就是說它的粒度更細。(具體實現有一些版本問題)
流控效果
流控效果非常簡單,意思就是限流掉的請求以什么方式來處理。分為三種:快速失敗、預熱、排隊等待。
- 快速失敗(默認):直接失敗,拋出異常。不做任何額外的處理,是最簡單的效果。
- Warm Up:預熱,它從開始閾值到最大閾值會有一個緩沖階段。一開始的閾值是最大閾值的 1/3,然后慢慢增長,直到最大閾值,適用于將突然增大的流量轉換為緩步增長的場景。
- 排隊等待:單機閾值為每秒通過數量,其他請求排隊等待,可設置超時時長,單位ms。當請求超過超時時間還未處理,就會被丟棄。
三、熔斷(降級)規則
降級規則就是設置當滿足什么條件的時候,對微服務進行熔斷降級。
四、熱點規則
Sentinel 可以對接口參數進行限流,以熱點規則來配置。
@GetMapping("/message1") @SentinelResource("params-api") public String message1(String name, String age) {return "message1"; }找到資源,設置熱點規則:
配置好后,點擊新增,那么當請求參數含有 name 時,就會受到該熱點規則的限制,因為熱點規則設置的參數索引是 0,即name:
五、授權規則(了解)
根據請求來源判斷是否放行。
流控應用:
標識具體來源,Sentinel 提供 RequestOriginParser 接口處理來源。只要 Sentinel 保護的接口資源被訪問,Sentinel 就會調用 RequestOriginParser 實現類去解析訪問來源。
授權類型:
- 若配置白名單,則只有請求來源在白名單內時才通過;
- 若配置黑名單,則請求來源在黑名單內時不通過,其余請求可以通過。
對于流控應用,需要自定義請求來源解析器。
@Component public class ServiceNameParser implements RequestOriginParser {/*** 解析請求來源,自定義規則*/@Overridepublic String parseOrigin(HttpServletRequest httpServletRequest) {String serviceName = httpServletRequest.getParameter("serviceName");return serviceName;} }然后配置授權規則:
像上圖,當Sentinel通過自定義的 parseOrigin() 方法取到的值為 “pc” 時,就放行請求,如果是其他請求就不予通過。若配置的是黑名單,則表示當 parseOrigin() 方法返回的值為 “pc” 時,不予通過,其他請求正常放行。
六、系統規則(了解)
系統保護規則是從應用級別的入口流量進行控制的,從單臺機器的總體 load 、RT、入口qps、cpu使用率和線程數五個維度監控應用數據,讓系統盡可能跑在最大吞吐量的同時保證系統整體的穩定性。
系統保護規則是應用緯度的,而不是資源緯度的,并且僅對入口流量(進入應用的流量)生效。
- Load(僅對類 Unix 系統生效):當系統 load1 超過閾值,且系統當前的并發線程數超過系統容量時,才觸發系統保護。系統容量由系統的maxQps * minRt 計算得出。設定參考值一般是 cpu cores * 2.5.
- RT:當單臺機器上所有入口流量的平均 RT 達到閾值時觸發系統保護,單位毫秒。
- 線程數:當單臺機器上所有入口流量的并發線程數達到閾值時觸發系統保護。
- 入口 QPS:當單臺機器上所有入口流量的 QPS 達到閾值時觸發系統保護。
- CPU 使用率:當單臺機器上所有入口流量的 CPU 使用達到閾值時觸發系統保護。
七、自定義異常返回
默認情況,所有的流控規則返回的錯誤信息都是一樣的,客戶端無法通過流控的異常返回執行特定的邏輯。為了解決這個問題,我們可以自定義異常返回信息,幫助請求方區分不同的限流異常。
Sentinel 提供了一個 UrlBlockHandler 接口,只需要實現該接口就可以輕松定義異常返回。其中 BlockException 就是所有限流異常的根接口。
import com.alibaba.csp.sentinel.adapter.servlet.callback.UrlBlockHandler; import com.alibaba.csp.sentinel.slots.block.BlockException; import com.alibaba.csp.sentinel.slots.block.flow.FlowException; import com.alibaba.fastjson.JSON;/*** 自定義限流返回信息*/ @Component public class SentinelFlowLimitConfig implements UrlBlockHandler {@Overridepublic void blocked(HttpServletRequest req, HttpServletResponse resp, BlockException e) throws IOException {// 避免中文亂碼問題resp.setContentType("application/json;charset=utf-8");DataResp dataResp = null;if (e instanceof FlowException) {dataResp = new DataResp(-1, "限流異常!");}resp.getWriter().write(JSON.toJSONString(dataResp));} }@Data @NoArgsConstructor @AllArgsConstructor class DataResp {private Integer code;private String msg; }
除了 FlowException 之外,還有其他幾種規則的限流異常,可以具體情況具體處理:
八、@SentinelResource
定義流控資源的注解,一般標記在方法上,常見于 controller 接口上。
除了可以指定資源名稱外,@SentinelResource 還可以指定 blockHandler 和 fallback 兩個屬性,
@GetMapping("/message1")@SentinelResource(value = "params-api",blockHandler = "message1Block",fallback = "message1Fallback")public String message1(String name, String age) {return "message1";}blockHandler 指定一個方法名,用于處理當流控規則觸發時對應異常需要處理的邏輯;
fallback 指定一個方法名,用于處理資源發生任何異常時需要處理的邏輯,相當于針對該接口特定的異常捕獲邏輯。
如果 blockHandler 已經攔截了流控規則產生的異常,就不會再執行 fallback 定義的異常邏輯。有點類似:
blockHandler 指定的方法要求必須和資源方法具有相同的返回值類型,以及參數列表,但允許參數列表最后一個參數加入一個 BlockException:
public String message1Block(String name, String age, BlockException e) {// 流控觸發后需要執行的邏輯 }同樣,fallback 指定的方法也要求必須和資源方法具有相同的返回值類型,以及參數列表,但允許參數列表最后一個參數加入一個 Throwable :
public String message1Fallback(String name, String age, Throwable e) {// 更通用的異常處理邏輯 }另外,還可以定義具體的處理類來專門針對對應資源維護具體的接口降級邏輯,具體參數是 blockHandlerClass、fallbackClass。
九、Sentinel 規則持久化(待補充)
官方文檔:動態規則擴展
Sentinel 采用 c/s 架構,Dashboard 為 s,微服務節點為 c。
Sentinel Dashboard 可以為每個 Sentinel 微服務客戶端設置各種限流監控規則,但這些規則默認是存儲在客戶端內存中,當客戶端應用重啟后,配置就會消失,極不穩定,所以,將規則持久化的需求是必要的。
本地文件數據源會定時輪詢文件的變更,讀取規則。這樣既可以本地直接修改文件來更新規則,也可以通過 Dashboard 來推送規則:
十、Feign 整合 Sentinel 實現容錯
啟動類添加 @EnableFeignClients 注解,yaml 配置添加如下配置:
當 findByPid 方法返回 -100時,就代表走了降級實現,我們可以返回下單失敗消息:
測試:正常調用 order --> product 微服務調用成功,可以返回皮大衣以及訂單id:
當關閉 product 微服務之后,就出現我們的降級響應:
這樣就實現了降級邏輯的處理,和前面 @SentinelResource 有類似的效果。
不過,這種 fallback 還是不理想。我們無法取得 Feign 調用失敗的真正異常,最好采用 fallbackFactory 來實現:
@Slf4j @Service public class ProductFallbackFactory implements FallbackFactory<ProductFeignClient> {@Overridepublic ProductFeignClient create(Throwable throwable) {return new ProductFeignClient() {@Overridepublic Product findByPid(Integer pid) {log.error("查詢商品信息失敗...");log.error("{}", throwable);Product product = new Product();product.setPid(-100);return product;}};} }當然,FeignClient 接口不要既指定了 fallback 又指定了 fallbackFactory,留一個即可:
@FeignClient(value = "service-product", // fallback = ProductFeignFallback.class,fallbackFactory = ProductFallbackFactory.class ) public interface ProductFeignClient {@GetMapping("/product/{pid}")Product findByPid(@PathVariable("pid") Integer pid); }總結
以上是生活随笔為你收集整理的Spring Cloud Alibaba —— Sentinel 详细使用的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: mysql8.0与mysql7.0_My
- 下一篇: python while语法结构_pyt