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