SpringCloud、SpringCloudAlibaba、SpringBoot版本選擇。為了避免各種千奇百怪的bug,我們還是采用官方推薦的畢業(yè)版本。
服務(wù)熔斷降級(jí)Sentinel
高并發(fā)請(qǐng)求模擬(這里我們使用contiperf來(lái)進(jìn)行測(cè)試)
修改tomcat配置最大線(xiàn)程數(shù)
server:
port: 8882
# 為了模擬高并發(fā)請(qǐng)求,將tomcat最大并發(fā)數(shù)修改為10
tomcat:
threads:
max: 10
引入測(cè)試依賴(lài)
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.7</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.databene</groupId>
<artifactId>contiperf</artifactId>
<version>2.1.0</version>
<scope>test</scope>
</dependency>
編寫(xiě)測(cè)試代碼
@RequestMapping("/thread")
public String thread() throws InterruptedException {
TimeUnit.SECONDS.sleep(2);
return "Cloud2OrderApp thread";
}
public class ContiPerfTest {
@Rule
public ContiPerfRule i = new ContiPerfRule();
// invocations 并發(fā)數(shù) threads 線(xiàn)程數(shù)
@Test
@PerfTest(invocations = 100, threads = 200)
// @Required(max = 100000, average = 250, totalTime = 100000)
public void test1() throws Exception {
RestTemplate restTemplate = new RestTemplate();
String result = restTemplate.getForObject("http://localhost:8882/thread", String.class);
System.out.println(result);
}
}
這里同時(shí)我們?cè)跒g覽器去請(qǐng)求該地址,響應(yīng)會(huì)變得很慢
測(cè)試結(jié)論:此時(shí)會(huì)發(fā)現(xiàn)由于thread接口囤積大量請(qǐng)求,導(dǎo)致index方法訪(fǎng)問(wèn)出現(xiàn)問(wèn)題,這就是服務(wù)雪崩的雛形。
服務(wù)雪崩
當(dāng)一個(gè)接口高頻訪(fǎng)問(wèn)耗費(fèi)完資源會(huì)影響到其他接口的正常訪(fǎng)問(wèn),這個(gè)場(chǎng)景擴(kuò)展到不同的微服務(wù)會(huì)導(dǎo)致服務(wù)雪崩
由于服務(wù)與服務(wù)之間的依賴(lài)性,故障會(huì)傳播,對(duì)整個(gè)微服務(wù)系統(tǒng)早長(zhǎng)城災(zāi)難性的嚴(yán)重后果,這就是故障的雪崩效應(yīng)。
雪崩發(fā)生的原因是多樣的,可能是設(shè)計(jì)的容量不合理,或者是高并發(fā)下某一個(gè)方法相應(yīng)很慢,或者某臺(tái)機(jī)器的資源消耗殆盡。我們無(wú)法完全杜絕雪崩源頭的發(fā)生,只有做好足夠的容錯(cuò)機(jī)制,保證在一個(gè)服務(wù)發(fā)生問(wèn)題,不會(huì)影響其他服務(wù)的正常運(yùn)作。
服務(wù)雪崩的容錯(cuò)方案(隔離、超時(shí)、限流、熔斷、降級(jí))
要防止雪崩擴(kuò)散,就要做好服務(wù)的容錯(cuò)機(jī)制。
常見(jiàn)的容錯(cuò)思路:隔離、超時(shí)、限流、熔斷、降級(jí)
隔離機(jī)制:
超時(shí)機(jī)制:
上有服務(wù)調(diào)用下游服務(wù)的時(shí)候,設(shè)置一個(gè)最大響應(yīng)時(shí)間,如果超時(shí),下游未做出響應(yīng)則自動(dòng)斷開(kāi)請(qǐng)求,釋放線(xiàn)程。
限流機(jī)制:
限制系統(tǒng)輸入輸出流量以達(dá)到保護(hù)系統(tǒng)目的。為了保證系統(tǒng)穩(wěn)定運(yùn)行,一旦達(dá)到閾值,就需要限流采取措施來(lái)完成限流目的。
熔斷機(jī)制:
當(dāng)下游服務(wù)因?yàn)樵L(fǎng)問(wèn)壓力過(guò)大而響應(yīng)變慢或者失敗,上游服務(wù)為保證系統(tǒng)整體可用,暫時(shí)切斷對(duì)下游服務(wù)調(diào)用。犧牲局部來(lái)保證整體可用性。熔斷一般有三種狀態(tài):
- 熔斷關(guān)閉狀態(tài)(Close):服務(wù)沒(méi)有故障時(shí),斷路器的狀態(tài),對(duì)調(diào)用方不做任何限制
- 熔斷開(kāi)啟狀態(tài)(Open):后續(xù)對(duì)該服務(wù)接口調(diào)用不再經(jīng)過(guò)網(wǎng)絡(luò),直接執(zhí)行本地fallback方法
- 半熔斷狀態(tài)(Half-Open):嘗試恢復(fù)服務(wù)調(diào)用,允許有限的流量調(diào)用該服務(wù),并監(jiān)控調(diào)用成功率。如果成功率達(dá)到預(yù)期,說(shuō)明服務(wù)已經(jīng)恢復(fù),進(jìn)入熔斷關(guān)閉狀態(tài)。如果成功率仍然很低,則重新進(jìn)入熔斷開(kāi)啟狀態(tài)。
降級(jí)機(jī)制:
降級(jí)就是給服務(wù)提供一個(gè)最低的兜底方案,一旦服務(wù)無(wú)法正常調(diào)用,則使用該兜底方案。
常見(jiàn)的容錯(cuò)組件
Hystrix:Netflix開(kāi)源的延遲容錯(cuò)庫(kù)。隔離訪(fǎng)問(wèn)遠(yuǎn)程系統(tǒng)、服務(wù),防止級(jí)聯(lián)失敗,提高系統(tǒng)可用性與容錯(cuò)性。
Resilience4J:輕量、簡(jiǎn)單、文檔清晰、豐富的熔斷工具。Hystrix官方推薦替代方案。支持SpringBoot1/2,支持Prometheus監(jiān)控。
Sentinel:alibaba開(kāi)源斷路器實(shí)現(xiàn)。穩(wěn)定。分布式系統(tǒng)的流量防衛(wèi)兵。
Sentinel
一套用于服務(wù)容錯(cuò)的綜合性解決方案。以流量為切入點(diǎn),從流量控制、熔斷降級(jí)、系統(tǒng)負(fù)載保護(hù)等多維度來(lái)保護(hù)系統(tǒng)穩(wěn)定性。
Sentinel特征
- 豐富的應(yīng)用場(chǎng)景:秒殺、消息削峰、集群流量控制、實(shí)時(shí)熔斷下游不可用應(yīng)用等
- 完備的實(shí)時(shí)監(jiān)控:可以看到應(yīng)用的單臺(tái)機(jī)器秒級(jí)數(shù)據(jù),500臺(tái)以下規(guī)模的集群匯總運(yùn)行情況。
- 廣泛的開(kāi)源生態(tài):開(kāi)箱即用的與其他開(kāi)源庫(kù)整合模塊。引入依賴(lài)即可使用。
Sentinel倆大部分
- 核心庫(kù)(Client):不依賴(lài)任何框架,可以運(yùn)行于所有Java運(yùn)行時(shí)環(huán)境。同時(shí)對(duì)Dubbo/SpringCloud等框架有較好支持。
- 控制臺(tái)(Dashboard):基于SpringBoot開(kāi)發(fā),打包可以直接運(yùn)行,無(wú)需tomcat容器。
項(xiàng)目集成Sentinel與Sentinel控制面板
項(xiàng)目集成Sentinel
引入依賴(lài)
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
<version>${spring-cloud-alibaba.version}</version>
</dependency>
增加配置
spring:
cloud:
sentinel:
transport:
port: 8719 # 與控制臺(tái)交流的端口,隨意指定一個(gè)未使用的端口即可
dashboard: localhost:8080 # 指定控制臺(tái)服務(wù)的地址
安裝Sentinel控制臺(tái)
下載
下載安裝包:https://github.com/alibaba/Sentinel/releases/tag
sentinel-dashboard-2.0.0-alpha-preview.jar :https://github.com/alibaba/Sentinel/releases/tag/2.0.0-alpha
啟動(dòng)
# 直接使用java -jar命令啟動(dòng)項(xiàng)目(控制臺(tái)本身是一個(gè)SpringBoot項(xiàng)目)
# -Dserver.port=8080 指定端口。
# -Dcsp.sentinel.dashboard.server=localhost:8080 指定控制臺(tái)地址和端口,會(huì)自動(dòng)向該地址發(fā)送心跳包。地址格式為:hostIp:port 配置成localhost:8080即監(jiān)控自己
# -Dproject.name=sentinel-dashboard 指定Sentinel控制臺(tái)程序顯示名稱(chēng)
java -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard.jar
訪(fǎng)問(wèn):地址localhost:8080(默認(rèn)用戶(hù)/密碼:sentinel/sentinel)
如果出不來(lái)這個(gè)監(jiān)控項(xiàng),要多訪(fǎng)問(wèn)幾次項(xiàng)目
Sentinel功能使用
流控規(guī)則限制
資源名稱(chēng):唯一名稱(chēng),默認(rèn)是請(qǐng)求路徑,可以自定義
針對(duì)來(lái)源:針對(duì)哪個(gè)微服務(wù)進(jìn)行限流,默認(rèn)default不區(qū)分,全部限制
是否集群:
閾值類(lèi)型/單機(jī)閾值:
- QPS(每秒請(qǐng)求數(shù)):當(dāng)調(diào)用你接口QPS達(dá)到閾值,進(jìn)行限流
- 線(xiàn)程數(shù):當(dāng)調(diào)用接口線(xiàn)程數(shù)達(dá)到閾值,進(jìn)行限流
多訪(fǎng)問(wèn)幾次就會(huì)被流控
流控維度
Sentinel規(guī)則種類(lèi)
流控規(guī)則、降級(jí)規(guī)則、熱點(diǎn)規(guī)則、系統(tǒng)規(guī)則、授權(quán)規(guī)則
Sentinel控制實(shí)現(xiàn)原理AOP(熱插拔、責(zé)任鏈模式)
流量控制的原理:監(jiān)控應(yīng)用流量的QPS或并發(fā)線(xiàn)程數(shù)等指標(biāo),當(dāng)達(dá)到指定閾值時(shí)對(duì)流量進(jìn)行控制,以避免被瞬時(shí)流量高峰沖垮,從而保證應(yīng)用的高可用。
流控模式
- 直接(默認(rèn)):當(dāng)接口達(dá)到限流條件時(shí),開(kāi)啟限流
- 關(guān)聯(lián):當(dāng)關(guān)聯(lián)的資源達(dá)到限流條件時(shí),開(kāi)啟限流(適合做應(yīng)用讓步)
- 鏈路:當(dāng)從某個(gè)接口過(guò)來(lái)的資源達(dá)到限流條件時(shí),開(kāi)啟限流
關(guān)聯(lián)流控模式
數(shù)據(jù)庫(kù)讀寫(xiě)操作相互影響。如果寫(xiě)操作過(guò)多,會(huì)造成讀的性能下降。
或者比如下單接口后調(diào)用支付接口,如果下單訪(fǎng)問(wèn)過(guò)多占用支付接口性能。
鏈路流控模式
鏈路流控模式是指,當(dāng)某個(gè)接口過(guò)來(lái)的資源達(dá)到限流條件時(shí),開(kāi)啟限流。
案例
- 在配置文件中關(guān)閉sentinel的web-context-unify
spring:
cloud:
sentinel:
transport:
web-context-unify: false
流控效果
- 快速失?。J(rèn)):直接失敗,拋出異常,不做任何額外處理,是最簡(jiǎn)單的結(jié)果。
- WarmUp:從開(kāi)始閾值到最大QPS會(huì)有一個(gè)緩沖階段,一開(kāi)始的閾值是最大QPS的1/3,然后慢慢增長(zhǎng),知道最大閾值,適用于突然增大的流量轉(zhuǎn)換為緩慢增長(zhǎng)的場(chǎng)景
- 排隊(duì)等待:讓請(qǐng)求以均勻的速度通過(guò),單機(jī)閾值以每秒通過(guò)數(shù)量,其余的排隊(duì)等待,會(huì)讓設(shè)置一個(gè)超時(shí)時(shí)間,當(dāng)請(qǐng)求超過(guò)時(shí)間還未處理則會(huì)被丟棄。
熔斷降級(jí)規(guī)則
- 慢調(diào)用比例:響應(yīng)時(shí)間超長(zhǎng)的請(qǐng)求數(shù)比例
- 異常比例:請(qǐng)求異常比例
- 異常數(shù):請(qǐng)求異常數(shù)量
慢調(diào)用比例
下面配置表示在1秒內(nèi)超過(guò)5個(gè)請(qǐng)求,且這些請(qǐng)求中的響應(yīng)時(shí)間大于最大RT時(shí)間的10%就觸發(fā)熔斷。在接下來(lái)的10秒內(nèi)都不調(diào)用真實(shí)方法處理。直接走降級(jí)方法。
異常比例
下面配置表示在1秒內(nèi)有超過(guò)1個(gè)請(qǐng)求的10%就觸發(fā)熔斷,熔斷時(shí)間間隔5秒。
異常數(shù)
下面配置表示1秒內(nèi)5個(gè)請(qǐng)求中,有三次異常就觸發(fā)熔斷。熔斷間隔時(shí)間5秒。
熱點(diǎn)規(guī)則(注意加上注解 @SentinelResource)
熱點(diǎn)是指經(jīng)常訪(fǎng)問(wèn)的數(shù)據(jù)。很多時(shí)候我們希望統(tǒng)計(jì)某個(gè)熱點(diǎn)數(shù)據(jù)中訪(fǎng)問(wèn)頻次最高的top n條數(shù)據(jù),并推起進(jìn)行訪(fǎng)問(wèn)控制。
如:統(tǒng)計(jì)一段時(shí)間內(nèi)最常購(gòu)買(mǎi)的商品ID并進(jìn)行限制。對(duì)一段時(shí)間內(nèi)頻繁訪(fǎng)問(wèn)的用戶(hù)ID進(jìn)行限制。防止刷贊等。
熱點(diǎn)參數(shù)限流會(huì)統(tǒng)計(jì)傳入?yún)?shù)中的熱點(diǎn)參數(shù),并根據(jù)配置的限流閾值與模式,對(duì)包含熱點(diǎn)參數(shù)
授權(quán)規(guī)則
很多時(shí)候,需要根據(jù)調(diào)用來(lái)源來(lái)判斷你請(qǐng)求是否允許放行,這時(shí)候可以使用sentinel的來(lái)源訪(fǎng)問(wèn)控制(黑白名單)功能。來(lái)源訪(fǎng)問(wèn)控制根據(jù)資源的請(qǐng)求(origin)限制資源是否通過(guò),若配置白名單則只有請(qǐng)求來(lái)源位于白名單內(nèi)時(shí)可以通過(guò),若配置黑名單,則請(qǐng)求來(lái)源位于黑名單時(shí)不通過(guò),其余可以通過(guò)。
系統(tǒng)規(guī)則
- Load自適應(yīng)(僅對(duì)Linux/Unix機(jī)器有效):系統(tǒng)load1作為啟發(fā)指標(biāo),進(jìn)行自適應(yīng)系統(tǒng)保護(hù),當(dāng)系統(tǒng)load1超過(guò)設(shè)定的閾值,且系統(tǒng)當(dāng)前并發(fā)線(xiàn)程數(shù)超過(guò)估算的系統(tǒng)容量才會(huì)觸發(fā)系統(tǒng)保護(hù)(BBR階段)。
系統(tǒng)容量由系統(tǒng)的maxQPS * minRT估算。設(shè)定參考值一般是CPU cores * 2.5. - CPU usage:當(dāng)系統(tǒng)cpu使用率超過(guò)閾值就觸發(fā)保護(hù)。(取值0.0-1.0)比較靈敏。
- 平均RT:當(dāng)單臺(tái)機(jī)器上所有入口流量的平均RT達(dá)到閾值就觸發(fā)系統(tǒng)保護(hù),單位是毫秒。
- 并發(fā)線(xiàn)程數(shù):當(dāng)單臺(tái)機(jī)器上所有入口流量的并發(fā)線(xiàn)程數(shù)達(dá)到閾值就觸發(fā)系統(tǒng)保護(hù)。
- 入口QPS:當(dāng)單臺(tái)機(jī)器上所有入口流量的QPS達(dá)到閾值就觸發(fā)系統(tǒng)保護(hù)。
自定義Sentinel返回值
當(dāng)前面設(shè)定規(guī)則沒(méi)有滿(mǎn)足,可以自定義異常返回。文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-403052.html
- FlowException :限流異常
- DegradeException :降級(jí)異常
- ParamFlowException : 參數(shù)限流異常
- AuthorityException : 授權(quán)異常
- SystemBlockException : 系統(tǒng)負(fù)載異常
package com.hx.sentinel.error;
import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.BlockExceptionHandler;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityException;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException;
import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowException;
import com.alibaba.csp.sentinel.slots.system.SystemBlockException;
import com.alibaba.fastjson.JSON;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.commons.lang.CharSet;
import org.apache.http.Consts;
import org.apache.http.entity.ContentType;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.nio.charset.Charset;
/**
* @author Huathy
* @date 2023-04-04 23:58
* @description
*/
@Component
public class SentinelExceptionHandler implements BlockExceptionHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception {
Result result = null;
if (e instanceof FlowException) {
result = new Result(500101, "接口限流");
} else if (e instanceof DegradeException) {
result = new Result(500102, "接口降級(jí)");
} else if (e instanceof ParamFlowException) {
result = new Result(500101, "接口參數(shù)限流");
} else if (e instanceof AuthorityException) {
result = new Result(500101, "授權(quán)異常");
} else if (e instanceof SystemBlockException) {
result = new Result(500101, "系統(tǒng)負(fù)載異常");
} else {
result = new Result(500101, e.getMessage());
}
response.setCharacterEncoding(Consts.UTF_8.name());
response.setContentType(ContentType.APPLICATION_JSON.getMimeType());
response.getWriter().write(JSON.toJSONString(result));
}
@Data
@AllArgsConstructor
@NoArgsConstructor
class Result {
int code;
String msg;
}
}
@SentinelResource 使用了解
用于定義資源,并提供可選的異常處理和fallback配置項(xiàng)。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-403052.html
@RestController
public class AnnoController {
// 需求:當(dāng)觸發(fā)流控規(guī)則后,默認(rèn)向拋出異常。
// 此時(shí)業(yè)務(wù)需要在拋出異常前,進(jìn)行額外業(yè)務(wù)處理?;蚍祷啬J(rèn)參數(shù)
@RequestMapping("/anno1")
@SentinelResource(value = "/anno1", blockHandler = "anno1BlockHandler", fallback = "anno1Fallback")
public Map<String, Object> anno1(String pm) {
if(pm == null || "".equals("")){
throw new RuntimeException("參數(shù)為空!");
}
Map<String, Object> res = new HashMap<>();
res.put("code", 200);
res.put("msg", "請(qǐng)求成功");
res.put("param", pm);
return res;
}
/**
* 當(dāng)觸發(fā)流控規(guī)則之后,立即觸發(fā)該方法。
* 需要注意該handler方法的參數(shù)列表要與原方法一致,并在最后加上異常參數(shù)BlockException ex
*/
public Map<String, Object> anno1BlockHandler(String param, BlockException e) {
System.out.println("anno1 流控觸發(fā)");
Map<String, Object> res = new HashMap<>();
res.put("code", 403);
res.put("msg", "觸發(fā)流控默認(rèn)返回方法");
res.put("param", param);
res.put("exception", e);
return res;
}
/**
* 當(dāng)anno1方法執(zhí)行報(bào)錯(cuò)的時(shí)候,立即觸發(fā)該方法
* @param param
* @param e
* @return
*/
public Map<String, Object> anno1Fallback(String param, Throwable e) {
Map<String, Object> res = new HashMap<>();
res.put("code", 500);
res.put("msg", "處理出錯(cuò),默認(rèn)返回");
res.put("param", param);
res.put("exception", e);
return res;
}
}
Feign集成Sentinel走降級(jí)
在配置文件開(kāi)啟支持
feign:
sentinel:
enabled: true
編寫(xiě)降級(jí)容錯(cuò)類(lèi)
@Component
public class ProductFallService implements ProductService {
@Override
public String index() {
return "熔斷降級(jí)了!";
}
}
為feign客戶(hù)端配置降級(jí)容錯(cuò)類(lèi)
@FeignClient(value = "cloud2-product-server", fallback = ProductFallService.class)
public interface ProductService {
@GetMapping("/")
String index();
}
到了這里,關(guān)于SpringCloud學(xué)習(xí)6(Spring Cloud Alibaba)斷路器Sentinel熔斷降級(jí)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!