服務雪崩
由於某一服務 導致整條服務鏈像滾雪球一樣 導致整條服務都宕機不可用
例如 有服務ABCD 服務A調用服務B 服務B調用服務C 服務C調用服務D
由於某種原因 服務B宕機了 由於服務B宕機了 無法訪問服務C 服務D 致使大量請求累計到服務B 最后致使整條服務鏈宕機
解決方案
限流
設置一個流量閾值 如果超過這個閾值就進行降級或其他處理 這樣就降低了服務壓力
線程隔離
類似限流 不同的是限制線程數量 滿了的話就進行降級或其他處理
信號隔離
限制並發的訪問數量
熔斷和降級
熔斷是指 若某一服務不可用 則立即返回 避免大量請求堆積在不可用服務 與超時不同 超時則進行訪問不可用服務 等待服務修復再打開熔斷器 繼續訪問
降級處理是兜底方法 服務不可用則可以調用降級方法 進行一些處理
sentail的使用
快速使用
引入sentinel的依賴
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-core</artifactId>
<version>1.8.2</version>
</dependency>
資源是sentinel的核心概念 資源就是被保護的內容 定義資源名稱為sayHello 若業務正常則直接返回hello world 若遇到流控則執行catch語句段
Entry entry = null;
try {
entry = SphU.entry("sayHello");
return "Hello world";
} catch (BlockException e) {
e.printStackTrace();
return "被限流";
} finally {
if (entry != null) {
entry.exit();
}
}
之后在定義一個流控規則 FlowRule
表示流量控制規則 setResource方法設置為哪個資源進行流控 setGrade設置流控規則為QPS
若QPS > 1 則被流控
@PostConstruct
private static void initFlowRules() {
List<FlowRule> rules = new ArrayList<>();
FlowRule rule = new FlowRule();
rule.setResource("sayHello");
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
// Set limit QPS to 20.
rule.setCount(1);
rules.add(rule);
FlowRuleManager.loadRules(rules);
}
正常執行
QPS > 1
使用注解來定義資源以及流控 降級
導入依賴
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-annotation-aspectj</artifactId>
<version>1.8.2</version>
</dependency>
根據文檔 首先得注入一個Bean
@Configuration
public class SentinelAspectConfiguration {
@Bean
public SentinelResourceAspect sentinelResourceAspect() {
return new SentinelResourceAspect();
}
}
value表示資源名稱 blockHandler表示觸發限流后執行的方法 fallback觸發異常是執行的方法 方法必須為public 返回參數與返回值必須與原方法一致 fallback可以多添加一個throwable參數
具體規則文檔有寫: https://sentinelguard.io/zh-cn/docs/annotation-support.html
@GetMapping("sayName")
@SentinelResource(value = "sayName", blockHandler = "sayNameBlockHandler", fallback = "sayNameFallbackHandle")
public String sayName() {
int a = 12 / 0;
return "my name is lyra heartstrings";
}
public String sayNameFallbackHandle(Throwable e) {
e.printStackTrace();
return "異常";
}
public String sayNameBlockHandler(BlockException e) {
e.printStackTrace();
return "被限流";
}
@PostConstruct
public static void initFlowRules() {
List<FlowRule> rules = new ArrayList<>();
FlowRule rule = new FlowRule();
rule.setResource("sayName");
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
// Set limit QPS to 20.
rule.setCount(1);
rules.add(rule);
FlowRuleManager.loadRules(rules);
}
熔斷降級
當服務熔斷時 將調用降級方法 原方法將不會訪問
當熔斷時間執行完畢 服務將開啟半熔斷模式 這是如果客戶端發送一次請求 還是失敗的話 繼續觸發熔斷
基本和流控規則一樣 不同的是 降級規則為DegradeRule
例子中使用的為異常數
降級參數
@PostConstruct
public void degradeRuleInit() {
List<DegradeRule> degradeRules = new ArrayList<>();
DegradeRule degradeRule = new DegradeRule();
degradeRule.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT);
degradeRule.setResource("sayName");
// 30秒內2個請求有2個異常 則進行熔斷 執行降級方法
degradeRule.setCount(2);
// 最小請求數2個才記錄熔斷規則
degradeRule.setMinRequestAmount(2);
degradeRule.setStatIntervalMs(1000 * 30);
// 熔斷一分鍾 一分鍾后開啟版熔斷模式 開啟半熔斷模式后 若之后再次發送一個請求 若請求繼續失敗 則繼續進行熔斷
degradeRule.setTimeWindow(1000 * 60);
degradeRules.add(degradeRule);
DegradeRuleManager.loadRules(degradeRules);
}
sentinel dashboard
- 下jar包
https://github.com/alibaba/Sentinel/releases
- 根據配置參數進行配置並運行
https://github.com/alibaba/Sentinel/wiki/%E6%8E%A7%E5%88%B6%E5%8F%B0#%E6%8E%A7%E5%88%B6%E5%8F%B0%E9%85%8D%E7%BD%AE%E9%A1%B9
我這里設置端口為8080 帳號為setinel 密碼為365373011
java -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=sentinel-dashboard -Dsentinel.dashboard.auth.username=sentinel -Dsentinel.dashboard.auth.password=365373011 -jar sentinel-dashboard-1.8.2.jar
將服務注冊到sentinel dashboard中
- 導入依賴
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-transport-simple-http</artifactId>
<version>1.8.2</version>
</dependency>
- 添加jvm參數
sping cloud aibaba整合
導入依賴
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
編寫sentinel dashboard服務以及端口號
spring:
application:
name: provider
cloud:
sentinel:
transport:
dashboard: 10.0.4.3:8080
流控規則
應用場景
- 服務調用端有大量的流量
- 不同的調用者
- 網關接口
使用
在簇點鏈路 點擊相應的接口 添加限流規則
然后根據資源 添加流控方法
線程流控
服務調用這向服務提供者發送請求 由於因網絡原因或慢sql等其他原因 導致服務提供者遲遲無法返回響應 導致大量線程累計到服務調用方
這時就需要線程流控了
線程流控數為1時只能有有一個用戶同時進行請求 兩個或兩個以上的用戶就會被限流
關聯流控
一邊用於兩個請求搶占資源 兩個資源同時使用 系統會變慢 這時 需要停掉一個請求 使另個請求同行
如 支付和下單兩個操作 當下單和支付並發執行時 訂單過多 來不及處理 這時可以將下單操作進行限流 當訂單處理完畢后 再繼續進行下單操作
有兩個接口 一個是get 用於模擬支付操作 一個get用於模擬查詢訂單信息操作
@GetMapping("/get")
public String get() {
return "查詢";
}
@GetMapping("/insert")
public String insert() {
return "插入";
}
當下單操作達到閾值時 將查詢操作進行限流 以提高下單操作的執行速度
鏈路限流
根據調用方法來進行限流 controller調用service時 不同的controller調用相同的service 根據限流來設置哪個controller可以正常通過不受限流影響
在appliction中設置web-context-unify: false
新建一個service 接口 資源名稱為getUser
@SentinelResource(value = "getUser", blockHandler = "getUserHandler")
@Override
public String getUser() {
return "查詢用戶";
}
public String getUserHandler(BlockException e) {
return "限流";
}
聲明兩個controller接口
@GetMapping("/getLyra")
public String getLyra() {
return userService.getUser();
}
@GetMapping("/getBonBon")
public String getBonBon() {
return userService.getUser();
}
設置限流規則
當getBonBon調用getUser方法到達閾值時 進行限流
預熱流控(激增流量)
避免一下激增流量 打爆服務器 緩慢增加請求 達到設置的閾值
如 一個商品 在平常時 無人問津 緩存區中也沒有這個商品的緩存信息 當雙十一進行秒殺時 因為緩存中沒有這個商品的信息 所以要從數據庫中查詢 當大量的請求訪問數據庫時 容易將數據庫打停機
可以十一預熱流控來解決這個問題
一次先放行少量的請求 緩慢將請求增大 慢慢的訪問數據庫並將數據庫中差到的信息商品保存到緩存中 之后的請求直接訪問緩存即可 避免了緩存擊穿
計算公式為閾值/3 然后緩慢增加到閾值數
排隊等待(脈沖流量)
突然大量流量然后空閑一段時間 然后大量流量進入一直循環
排隊等待用於利用中間的空閑時間 來進行處理請求
沒有使用排隊等待前的脈沖流量 可以看到每次發生請求都有一段時間的空閑時間 我們可以使用排隊等待來利用這段空閑時間
使用了排隊等待之后
超時時間表示 若在6000ms內 10個QPS執行完畢 在讓10個請求進入
若超時 直接失敗
熔斷
熔斷一邊用在服務調用者 也就是consumer
慢調用比例
超過 最大rt時間為慢調用 最小請求數為觸發熔斷的最小請求數 大於這個數的請求 才有資格被熔斷
表示在1000ms中 有10%的慢調用請求就觸發熔斷
異常比例: 按異常比例進行熔斷 到達異常比例數 觸發熔斷
異常數: 根據異常數進行熔斷 到達異常數 觸發熔斷
統一流控異常
實現BlockExceptionHandler
接口並注入到方法中 然后根據流量異常 返回響應內容
package com.lyra.provider.exception;
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.nacos.common.http.param.MediaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.lyra.provider.domain.Result;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Component
public class MyBlockHandlerException implements BlockExceptionHandler {
private Logger logger = LoggerFactory.getLogger(MyBlockHandlerException.class);
@Override
public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, BlockException e) throws Exception {
logger.info("rule: ==========" + e.getRule());
Result result = null;
if (e instanceof FlowException) {
result = Result.errorResult(400, "被流控");
} else if (e instanceof ParamFlowException) {
result = Result.errorResult(401, "熱點參數流控");
} else if (e instanceof DegradeException) {
result = Result.errorResult(402, "被降級");
} else if (e instanceof AuthorityException) {
result = Result.errorResult(403, "授權規則未通過");
} else if (e instanceof SystemBlockException) {
result = Result.errorResult(404, "觸發系統保護規則");
}
httpServletResponse.setContentType(MediaType.APPLICATION_JSON);
httpServletResponse.getWriter().write(new ObjectMapper().writeValueAsString(result));
}
}
熱點限流
一般用於限流方法參數中出現的高頻數值進行限流
業務場景: 商品秒殺操作 如iphone之類的熱門商品一定會被高頻搜索 可以設置對iphone進行限流
不知道為什么dashboard一直設置不成功
dashboard只要添加熱點規則 然后添加熱點參數即可完成 不知道為什么一直無法保存
參素索引為方法所在的下標 單擊閾值為參數所出現的QPS 統計時長為每x秒進行一次統計QPS
這個方法的name參數 索引就為0
還可以對參數中的指定值進行限流
當參數為Dreamer時 切閾值大於2 則進行限流、
使用代碼進行配置 根據文檔 https://github.com/alibaba/Sentinel/wiki/%E7%83%AD%E7%82%B9%E5%8F%82%E6%95%B0%E9%99%90%E6%B5%81
將熱點配置添加即可
@PostConstruct
public void paramFlowRoleSet() {
List<ParamFlowRule> paramFlowItems = new ArrayList<>();
ParamFlowRule rule = new ParamFlowRule("sayHello");
rule.setParamIdx(0);
rule.setCount(10);
ParamFlowItem paramFlowItem = new ParamFlowItem();
paramFlowItem.setObject("Dreamer");
paramFlowItem.setCount(2);
rule.setParamFlowItemList(Collections.singletonList(paramFlowItem));
paramFlowItems.add(rule);
ParamFlowRuleManager.loadRules(paramFlowItems);
}
系統保護規則
因不明確的原因設置一個兜底方法 用於保護系統
可以以一下參數進行設置
Load 自適應(僅對 Linux/Unix-like 機器生效):系統的 load1 作為啟發指標,進行自適應系統保護。當系統 load1 超過設定的啟發值,且系統當前的並發線程數超過估算的系統容量時才會觸發系統保護(BBR 階段)。系統容量由系統的 maxQps * minRt 估算得出。設定參考值一般是 CPU cores * 2.5。
CPU usage(1.5.0+ 版本):當系統 CPU 使用率超過閾值即觸發系統保護(取值范圍 0.0-1.0),比較靈敏。
平均 RT:當單台機器上所有入口流量的平均 RT 達到閾值即觸發系統保護,單位是毫秒。
並發線程數:當單台機器上所有入口流量的並發線程數達到閾值即觸發系統保護。
入口 QPS:當單台機器上所有入口流量的 QPS 達到閾值即觸發系統保護。
sentinel整合open-fign進行降級
消費者調用服務提供者 服務提供者產生異常 會將異常傳遞給消費者 我們不希望服務提供者將異常傳遞給消費者 可以使用feign進行降級處理
首先feign服務導入依賴
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
在consumer開啟sentinel支持
feign:
sentinel:
enabled: true
降級方法實現feign接口
@Component
public class TestControllerFallBack implements TestControllerAPI {
@Override
public String sayHello(String name) {
return "feign降級";
}
}
feign接口 feign client注解添加faillback類
@FeignClient(name = "provider", fallback = TestControllerFallBack.class)
public interface TestControllerAPI {
@GetMapping("/sayHello")
public String sayHello(@RequestParam("name") String name);
}
服務提供者拋出一個異常
@Override
public String sayHello(@RequestParam String name) {
int a = 10 / 0;
return "My name is: " + name;
}
然后消費者調用