SpringCloud中Sentinel基礎(chǔ)場景和異常處理方式
Sentinel 是一個由 阿里巴巴 開源的分布式系統(tǒng)流量控制組件,專注于為微服務(wù)架構(gòu)提供流量控制、熔斷降級、系統(tǒng)負(fù)載保護等功能。
它特別適用于高并發(fā)、高可用性的分布式系統(tǒng),能夠幫助開發(fā)者保護系統(tǒng)免于因流量過載、系統(tǒng)崩潰、依賴不可用等情況而導(dǎo)致的服務(wù)不可用。
一、Sentinel 的核心功能
流量控制(Traffic Control):
- 限流:在服務(wù)接口或者資源上進行流量限制,確保系統(tǒng)在高負(fù)載情況下不會崩潰。
- QPS(每秒查詢數(shù)):對每秒請求數(shù)進行限制,超過設(shè)定閾值則拒絕請求。
- 并發(fā)線程數(shù)控制:控制在某個資源上同時處理請求的線程數(shù),避免系統(tǒng)資源被過度消耗。
熔斷降級(Circuit Breaker):
- 當(dāng)依賴服務(wù)出現(xiàn)故障時,Sentinel 可以自動進行熔斷處理,防止故障擴展,降低系統(tǒng)負(fù)擔(dān)。
- 根據(jù)定義的規(guī)則進行降級,比如當(dāng)請求的失敗率超過設(shè)定閾值時,自動進行服務(wù)降級處理。
- 降級模式:可以配置為流量的百分比降級或者固定的線程數(shù)降級。
系統(tǒng)負(fù)載保護(System Load Protection):
- Sentinel 可以通過系統(tǒng)負(fù)載(如 CPU、內(nèi)存、RT 等)來控制流量,確保在資源緊張時不會對系統(tǒng)產(chǎn)生更大的壓力。
- 可以根據(jù)系統(tǒng)的實時負(fù)載狀況動態(tài)調(diào)整請求的流量,保證系統(tǒng)的穩(wěn)定性。
熱點參數(shù)限流(Hot Spot Parameter Flow Control):
- 限流不僅僅可以基于資源來進行,也可以基于請求中的參數(shù)來進行。例如,可以限制某些特定參數(shù)的請求數(shù)量。
監(jiān)控與報警:
- Sentinel 提供了實時監(jiān)控能力,可以通過 Web 控制臺或者日志進行流量、系統(tǒng)負(fù)載、異常等信息的監(jiān)控。
- 支持與外部監(jiān)控系統(tǒng)(如 Prometheus、Grafana 等)集成,幫助開發(fā)者實時了解系統(tǒng)狀態(tài)。
集群模式:
- Sentinel 支持集群模式,適用于微服務(wù)架構(gòu)中的多服務(wù)間的流量控制。
- 支持將流量控制策略分布到整個集群中,保證整個系統(tǒng)的流量調(diào)度和熔斷降級的一致性。
二、使用 Sentinel
添加依賴:
在 Spring Boot 項目中集成 Sentinel,首先需要在 pom.xml 中添加相關(guān)的依賴:
<dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-core</artifactId> <version>1.8.3</version> </dependency>
使用 @SentinelResource 注解:
在方法上使用 @SentinelResource 注解來定義流量控制和熔斷降級邏輯:
@SentinelResource(value = "createOrder") @Override public Order createOrder(Long productId, Long userId) { Product product = getProductFromRemoteWithLoadBalanceAnnotation(productId); // 使用 Feign 完成遠(yuǎn)程調(diào)用 Product product = productFeignClient.getProductById(productId); Order order = new Order(); order.setId(1L); // 總金額 order.setTotalAmount(product.getPrice().multiply(new BigDecimal(product.getNum()))); order.setUserId(userId); order.setNickName("zhangsan"); return order; }
@SentinelResource(value = "createOrder")
:為 createOrder
方法添加了 Sentinel 的流量控制和熔斷降級功能。這意味著該方法在執(zhí)行時會受 Sentinel 管控,觸發(fā)流量控制或熔斷時,會執(zhí)行相應(yīng)的降級邏輯(如返回默認(rèn)值或調(diào)用備用方法)。
productFeignClient.getProductById(productId)
:使用 Feign 遠(yuǎn)程調(diào)用其他服務(wù)獲取產(chǎn)品信息。productFeignClient
是通過 Feign 客戶端定義的接口,能夠調(diào)用遠(yuǎn)程服務(wù) getProductById
方法來獲取指定 productId
的產(chǎn)品信息。
三、異常處理
其中異常處理流程主要分為三個大類:Web 接口的異常處理,@SentinelResource和 OpenFeign 調(diào)用的異常處理。涉及了不同的組件和處理方法:
1. Web 接口中的異常處理
在 Web 接口的異常處理中,主要使用 SentinelWebInterceptor 來攔截和處理請求中的異常。
1.1 SentinelWebInterceptor
- 這是 Sentinel 中用于 Web 接口流量控制的攔截器。當(dāng)請求的流量超出了預(yù)設(shè)的閾值(例如 QPS 限制),或者系統(tǒng)出現(xiàn)異常時,Sentinel 會自動觸發(fā)
BlockException
。 - 該攔截器可以捕獲限流(流量控制)和降級的異常,并通過
BlockExceptionHandler
進行處理。
1.2 默認(rèn) BlockExceptionHandler
BlockExceptionHandler
是 Sentinel 默認(rèn)的異常處理器。當(dāng)發(fā)生限流或降級時,會調(diào)用默認(rèn)的BlockExceptionHandler
進行處理,通常它會返回一個簡單的錯誤響應(yīng),提示用戶請求被限流或服務(wù)降級。- 處理邏輯通常是返回一個自定義的錯誤消息,或者直接返回一個 500 或 429 狀態(tài)碼,表明請求被限制。
1.3 自定義 BlockExceptionHandler
- 自定義異常處理器:如果默認(rèn)的處理方式不符合需求,可以通過實現(xiàn)
BlockExceptionHandler
接口來創(chuàng)建自定義的異常處理邏輯。 - 比如,你可以在自定義異常處理中加入詳細(xì)的日志記錄、告警機制、或者更復(fù)雜的錯誤響應(yīng)格式。
import com.fasterxml.jackson.databind.ObjectMapper; import com.alibaba.csp.sentinel.slots.block.BlockException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.PrintWriter; @Component public class MyBlockExceptionHandler implements BlockExceptionHandler { private ObjectMapper objectMapper = new ObjectMapper(); // 用于將錯誤信息轉(zhuǎn)換成 JSON 格式 @Override public void handle(HttpServletRequest request, HttpServletResponse response, String resourceName, BlockException e) throws Exception { // 獲取響應(yīng)的 PrintWriter 用于向客戶端輸出響應(yīng)內(nèi)容 PrintWriter writer = response.getWriter(); // 設(shè)置響應(yīng)的內(nèi)容類型為 JSON response.setContentType("application/json;charset=utf-8"); // 創(chuàng)建一個自定義的錯誤信息對象 R,包含錯誤的代碼和消息 R error = R.error(500, resourceName + " 被Sentinel 限制了, 原因: " + e.getMessage()); // 使用 ObjectMapper 將錯誤對象轉(zhuǎn)換為 JSON 字符串 String json = objectMapper.writeValueAsString(error); // 將 JSON 寫入響應(yīng)輸出流 writer.write(json); } }
@Component
注解:
- 該類使用
@Component
注解標(biāo)記為 Spring Bean,確保 Spring 能掃描到該類并進行自動注冊。 - 這個類實現(xiàn)了
BlockExceptionHandler
接口,作為 Sentinel 的自定義異常處理器。
ObjectMapper
實例化:
ObjectMapper
是 Jackson 庫中的一個類,用于將 Java 對象轉(zhuǎn)換為 JSON 字符串。- 我們在
handle
方法中使用它來將錯誤信息對象R
轉(zhuǎn)換為 JSON 格式的字符串。
handle
方法:
handle
方法會在BlockException
發(fā)生時調(diào)用,這是 Sentinel 的異常處理方法。它接收以下參數(shù):request
:當(dāng)前請求對象。response
:當(dāng)前響應(yīng)對象,用于輸出響應(yīng)。resourceName
:觸發(fā)限制的資源名稱(如 API 名稱)。e
:BlockException
,是 Sentinel 拋出的異常,表示請求被限流或熔斷等原因阻塞。
2. @SentinelResource 注解中的異常(常用于控制器以外的類上)
在使用 @SentinelResource
注解時,我們可以為特定資源配置 流量控制、降級 和 熔斷 等處理。
它可以和 blockHandler、fallback 配合使用,確保服務(wù)在流量限制或異常發(fā)生時進行降級處理。
2.1 SentinelResourceAspect
SentinelResourceAspect
是 Sentinel 的一個切面,它會在方法執(zhí)行時處理 流量控制(限流)和 異常降級。- 當(dāng)觸發(fā)
BlockException
時,會自動調(diào)用配置好的blockHandler
方法來處理限流異常;如果發(fā)生其他異常(如業(yè)務(wù)邏輯失?。?,則會調(diào)用fallback
方法進行降級處理。
2.2 blockHandler
blockHandler
是@SentinelResource
注解中的一個參數(shù),用于指定當(dāng)流量控制(限流)發(fā)生時,如何處理該異常。- 我們通過定義一個處理被阻斷邏輯的
blockHandler
方法并使用注解將其與原始請求方法關(guān)聯(lián)。 - resource標(biāo)注的資源沒有違反規(guī)則,則調(diào)用真實的業(yè)務(wù)邏輯去返回真實的數(shù)據(jù),如果違反規(guī)則被滲透的限制了,則調(diào)用block handler指定的方法返回兜底數(shù)據(jù),也就是control最終要么得到一個真實數(shù)據(jù),要么得到一個兜底數(shù)據(jù)。
@SentinelResource(value = "createOrder", blockHandler = "createOrderFallback") @Override public Order createOrder(Long productId, Long userId) { // 使用 Feign 完成遠(yuǎn)程調(diào)用 Product product = productFeignClient.getProductById(productId); Order order = new Order(); order.setId(1L); // 總金額 order.setTotalAmount(product.getPrice().multiply(new BigDecimal(product.getNum()))); order.setUserId(userId); order.setNickName("zhangsan"); order.setAddress("西安工業(yè)大學(xué)"); // 遠(yuǎn)程查詢商品列表 order.setProductList(Arrays.asList(product)); return order; } // 降級回調(diào) public Order createOrderFallback(Long productId, Long userId, BlockException e) { Order order = new Order(); order.setId(0L); order.setTotalAmount(new BigDecimal("0")); order.setUserId(userId); order.setNickName("未注冊用戶"); order.setAddress("異常信息: " + e.getClass()); return order; }
這段代碼保證了在請求被限流或發(fā)生異常時,能夠回退到一個默認(rèn)的響應(yīng),從而避免系統(tǒng)崩潰。具體通過 @SentinelResource
注解、blockHandler
機制實現(xiàn)了流量控制與異常降級處理。在控制后返回一個兜底回調(diào)數(shù)據(jù)。
2.3 fallback
fallback
是@SentinelResource
中的另一個參數(shù),用于指定當(dāng)服務(wù)出現(xiàn)異常時(如服務(wù)不可用或業(yè)務(wù)異常),如何進行服務(wù)降級處理。- 降級后的邏輯通常是提供備用數(shù)據(jù)或簡單的錯誤響應(yīng)。
最終總結(jié)起來,我們的最佳實戰(zhàn)用法就是SentinelResource
一般標(biāo)注在非control的這些層,你想要給哪些方法進行保護,你就加上這個注解,一旦違反規(guī)則以后,如果業(yè)務(wù)規(guī)定有兜底回調(diào)的數(shù)據(jù),那么就使用block handler去來指定兜底回調(diào),如果業(yè)務(wù)沒規(guī)定有兜底回調(diào),那我們也可以不用任何一種回調(diào)機制,直接讓異常拋給全局,由項目的spring boot全局異常處理器進行處理。
3. OpenFeign 調(diào)用中的異常處理
在 OpenFeign 的調(diào)用中,Sentinel 同樣提供了流量控制和熔斷降級的功能。
3.1 SentinelFeign.builder()
SentinelFeign.builder()
用于構(gòu)建 Feign 客戶端時集成 Sentinel 的流量控制和熔斷功能。當(dāng)進行 Feign 調(diào)用時,Sentinel 會自動為調(diào)用方法設(shè)置流量控制規(guī)則。- 你可以通過
SentinelFeign.builder()
配置流量控制和降級策略,并為 Feign 方法定義blockHandler
和fallback
。
3.2 fallback
- 在 Feign 調(diào)用中,
fallback
用于指定當(dāng)遠(yuǎn)程服務(wù)不可用或者請求超時時,如何降級。 - 可以為每個 Feign 調(diào)用定義
fallback
方法,確保即使遠(yuǎn)程服務(wù)不可用,系統(tǒng)也能提供備用的返回值。
Feign 客戶端接口
@FeignClient(value = "service-product", fallback = ProductFeignClientFallback.class) // feign客戶端 public interface ProductFeignClient { @GetMapping("/product/{id}") Product getProductById(@PathVariable("id") Long id); }
@FeignClient
:定義了一個 Feign 客戶端接口,value = "service-product"
表示它會向 service-product
服務(wù)發(fā)起請求,fallback
指定了降級,ProductFeignClientFallback.class
,當(dāng)遠(yuǎn)程調(diào)用失敗時會觸發(fā)該降級方法。
Feign 客戶端的降級處理:
@Component public class ProductFeignClientFallback implements ProductFeignClient { @Override public Product getProductById(Long id) { System.out.println("降級回調(diào)...."); Product product = new Product(); product.setId(id); product.setPrice(new BigDecimal("0")); product.setProductName("未知商品"); product.setNum(0); return product; } }
4. Spring Boot 異常處理
4.1 SpringBoot 異常處理
- Sentinel 在 Spring Boot 中的集成也允許我們通過 全局異常處理 來捕獲和處理來自 Sentinel 的
BlockException
。 - 在 Spring Boot 中,使用
@ControllerAdvice
可以集中處理所有的異常,包括BlockException
。如果請求被限流或服務(wù)降級,Spring Boot 會捕獲并處理這些異常,保證系統(tǒng)的高可用性
四、fallback 和 blockHandler 的區(qū)別
1. fallback
:
- 目的:
fallback
用于處理 業(yè)務(wù)異常,例如遠(yuǎn)程服務(wù)不可用、請求超時等。它提供一種備用的處理方式,以保證系統(tǒng)的可用性。 - 觸發(fā)時機:當(dāng)方法在正常執(zhí)行過程中發(fā)生異常時(例如
getProductById
方法無法獲取遠(yuǎn)程數(shù)據(jù)時),fallback
會被調(diào)用。 - 使用場景:當(dāng)服務(wù)請求失敗時,
fallback
會返回一個默認(rèn)值或一個特定的錯誤響應(yīng)。例如,產(chǎn)品查詢失敗時,返回一個“未知商品”的占位符。
2. blockHandler
:
- 目的:
blockHandler
用于處理 流量控制相關(guān)的異常,例如當(dāng)系統(tǒng)流量超出預(yù)定閾值時,Sentinel 會觸發(fā)限流或熔斷,調(diào)用blockHandler
來處理這些流量控制異常。 - 觸發(fā)時機:當(dāng) 流量控制(如限流、降級、熔斷)觸發(fā)時,
blockHandler
會被調(diào)用,用來處理流量被阻塞的情況。 - 使用場景:當(dāng)請求被流量控制機制(如限流)阻塞時,
blockHandler
會被觸發(fā),返回限流信息或適當(dāng)?shù)慕导夗憫?yīng)。例如,系統(tǒng)在高并發(fā)場景下超過閾值時,blockHandler
會返回“請求過多”的提示。
總結(jié)
fallback
主要是用于業(yè)務(wù)異?;蚍?wù)不可用時提供備用邏輯,確保服務(wù)在出現(xiàn)故障時仍能返回合適的結(jié)果。blockHandler
主要是處理流量控制相關(guān)的異常(如限流、熔斷等),確保系統(tǒng)在流量過大或異常情況下不會崩潰。
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
Java如何將字符串轉(zhuǎn)為數(shù)字int的三種方式詳析
這篇文章主要給大家介紹了關(guān)于Java如何將字符串轉(zhuǎn)為數(shù)字int的三種方式,在編程中我們經(jīng)常需要進行各種數(shù)據(jù)類型之間的轉(zhuǎn)換操作,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-10-10淺談SpringSecurity注解與AOP切面執(zhí)行順序
這篇文章主要介紹了淺談SpringSecurity注解與AOP切面執(zhí)行順序,引入Spring Security后,在Controller的方法中會出現(xiàn)Spring Security的方法注解與AOP同時存在的問題,這是就會設(shè)計順序問題,需要的朋友可以參考下2023-10-10Java實戰(zhàn)之仿天貓商城系統(tǒng)的實現(xiàn)
這篇文章主要介紹了如何利用Java制作一個基于SSM框架的迷你天貓商城系統(tǒng),文中采用的技術(shù)有JSP、Springboot、SpringMVC、Spring等,需要的可以參考一下2022-03-03SpringBoot前后端分離跨域問題:狀態(tài)碼403拒絕訪問解決辦法
這篇文章主要給大家介紹了關(guān)于SpringBoot前后端分離跨域問題:狀態(tài)碼403拒絕訪問的解決辦法,403是被服務(wù)器拒絕了,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-01-01在SpringBoot中使用@Value注解來設(shè)置默認(rèn)值的方法
Spring Boot提供了一種使用注解設(shè)置默認(rèn)值的方式,即使用 @Value 注解,下面這篇文章主要給大家介紹了關(guān)于如何在SpringBoot中使用@Value注解來設(shè)置默認(rèn)值的相關(guān)資料,需要的朋友可以參考下2023-10-10