一、Sentinel 介紹
隨着微服務的流行,服務和服務之間的穩定性變得越來越重要。 https://github.com/alibaba/Sentinel[Sentinel] 以流量爲切入點,從流量控制、熔斷降級、系統負載保護等多個維度保護服務的穩定性。
https://github.com/alibaba/Sentinel[Sentinel] 具有以下特徵:
- 豐富的應用場景: Sentinel 承接了阿里巴巴近 10 年的雙十一大促流量的核心場景,例如秒殺(即突發流量控制在系統容量可以承受的範圍)、消息削峯填谷、實時熔斷下游不可用應用等。
- 完備的實時監控: Sentinel 同時提供實時的監控功能。您可以在控制檯中看到接入應用的單臺機器秒級數據,甚至 500 臺以下規模的集羣的彙總運行情況。
- 廣泛的開源生態: Sentinel 提供開箱即用的與其它開源框架/庫的整合模塊,例如與 Spring Cloud、Dubbo、gRPC 的整合。您只需要引入相應的依賴並進行簡單的配置即可快速地接入 Sentinel。
- 完善的 SPI 擴展點: Sentinel 提供簡單易用、完善的 SPI 擴展點。您可以通過實現擴展點,快速的定製邏輯。例如定製規則管理、適配數據源等。
服務保護的基本概念
服務限流/熔斷
在高併發情況下,客戶端請求達到了一定的極限,也就是我們所設定的閾值,服務就會自動開啓自我保護機制,直接走我們的服務降級fallback方法,給客戶端一個友好提示。
服務降級
在高併發情況下,爲了防止用戶一直等待,給用戶一個友好提示。
服務的雪崩效應
默認情況下,tomcat/jetty服務器只有一個線程池去處理用戶請求。在高併發情況下,如果客戶端的所有請求都堆積在同一個接口上,線程池中的所有線程都用來處理這些請求,就會導致其他接口無法訪問。
服務隔離機制
服務隔離機制分爲兩種:信號量隔離和線程池隔離
信號量隔離:最多只能有一定的閾值的線程數來處理我們的請求,超過閾值就會拒絕請求
線程池隔離:每個服務接口都有獨立的線程池來處理請求,接口之間互不影響,缺點:佔用CPU資源較大
Sentinel和Hytrix區別
二、Sentinel 環境快速搭建
下載對應Sentinel-Dashboard
https://github.com/alibaba/Sentinel/releases/tag/1.7.1 運行即可。
默認賬號密碼:sentinel/sentinel
運行執行命令
java -Dserver.port=8718 -Dcsp.sentinel.dashboard.server=localhost:8718 -Dproject.name=sentinel-dashboard -Dcsp.sentinel.api.port=8719 -jar sentinel-dashboard-1.7.1.jar
三、Sentinel實現API動態限流
maven依賴
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alibaba-sentinel</artifactId>
<version>0.2.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
1.手動配置管理API限流接口
private static final String GETORDER_KEY = "getOrder";
@RequestMapping("/getOrder")
public String getOrders() {
Entry entry = null;
try {
entry = SphU.entry(GETORDER_KEY);
// 執行我們服務需要保護的業務邏輯
return "getOrder接口";
} catch (Exception e) {
e.printStackTrace();
return "該服務接口已經達到上線!";
} finally {
// SphU.entry(xxx) 需要與 entry.exit() 成對出現,否則會導致調用鏈記錄異常
if (entry != null) {
entry.exit();
}
}
}
限流配置放到項目自動加載
@Component
@Slf4j
public class SentinelApplicationRunner implements ApplicationRunner {
private static final String GETORDER_KEY = "getOrder";
@Override
public void run(ApplicationArguments args) throws Exception {
List<FlowRule> rules = new ArrayList<FlowRule>();
FlowRule rule1 = new FlowRule();
rule1.setResource(GETORDER_KEY);
// QPS控制在2以內
rule1.setCount(1);
// QPS限流
rule1.setGrade(RuleConstant.FLOW_GRADE_QPS);
rule1.setLimitApp("default");
rules.add(rule1);
FlowRuleManager.loadRules(rules);
log.info(">>>限流服務接口配置加載成功>>>");
}
}
2.註解形式配置管理Api限流
2.1 基於QPS限流
private static final String GETORDER_KEY = "getOrder";
/***
* @SentinelResource 流量規則資源名
* - blockHandler 限流/熔斷 出現異常 執行的方法
* - fallback 服務降級執行的方法
* -QPS=併發數/平均響應時間
* @return
*/
@SentinelResource(value = GETORDER_KEY,blockHandler = "getOrderQpsException")
@RequestMapping("/getOrder")
public String getOrders() {
return "getOrder接口";
}
/**
* 被限流後返回的提示
*
* @param e
* @return
*/
public String getOrderQpsException(BlockException e) {
e.printStackTrace();
return "該接口已經被限流啦!";
}
application.yml
server:
port: 8080
spring:
cloud:
sentinel:
transport:
port: 8719
dashboard: 192.168.75.137:8718
eager: true
application:
name: Sentinel-02
Sentinel控制檯中添加限流規則
2.2 基於併發數限流
/***
* 基於併發數量處理限流
* 併發數 = QPS*平均響應時間
* 每次最多隻會有一個線程處理該業務邏輯,超出該閾值的情況下,直接拒絕訪問。
* @return
*/
@SentinelResource(value = "getOrderThrad", blockHandler = "getOrderQpsException")
@RequestMapping("/getOrderThrad")
public String getgetOrderThread(){
System.out.println(Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "getOrderThrad";
}
Sentinel控制檯中添加限流規則
如果要在您的項目中引入 Sentinel,使用 group ID 爲 com.alibaba.cloud
和 artifact ID 爲 spring-cloud-starter-alibaba-sentinel
的 starter。
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
下面這個例子就是一個最簡單的使用 Sentinel 的例子:
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(ServiceApplication.class, args);
}
}
@RestController
public class TestController {
@GetMapping(value = "/hello")
@SentinelResource("hello")
public String hello() {
return "Hello Sentinel";
}
}
@SentinelResource 註解用來標識資源是否被限流、降級。上述例子上該註解的屬性 ‘hello’ 表示資源名。
@SentinelResource 還提供了其它額外的屬性如 blockHandler
,blockHandlerClass
,fallback
用於表示限流或降級的操作,更多內容可以參考 https://github.com/alibaba/Sentinel/wiki/%E6%B3%A8%E8%A7%A3%E6%94%AF%E6%8C%81[Sentinel註解支持]。
以上例子都是在 WebServlet 環境下使用的,Sentinel 目前已經支持 WebFlux,需要配合 spring-boot-starter-webflux
依賴觸發 sentinel-starter 中 WebFlux 相關的自動化配置。
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(ServiceApplication.class, args);
}
}
@RestController
public class TestController {
@GetMapping("/mono")
@SentinelResource("hello")
public Mono<String> mono() {
return Mono.just("simple string")
.transform(new SentinelReactorTransformer<>("otherResourceName"));
}
}
3. 基於熱點參數手動實現限流
添加maven依賴:
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-parameter-flow-control</artifactId>
<version>1.6.3</version>
<scope>compile</scope>
</dependency>
將限流規則,手動放入到項目啓動自動加載:
@Component
public class SentinelApplicationRunner implements ApplicationRunner {
private static final String GETORDER_KEY = "getOrder";
@Override
public void run(ApplicationArguments args) throws Exception {
// 定義熱點限流的規則,對第一個參數設置 qps 限流模式,閾值爲5
ParamFlowRule rule = new ParamFlowRule(GETORDER_KEY)
//熱點參數的索引
.setParamIdx(0)
//限流模式
.setGrade(RuleConstant.FLOW_GRADE_QPS)
//閾值
.setCount(2);
ParamFlowRuleManager.loadRules(Collections.singletonList(rule));
}
}
controller層:
@RestController
public class IndexController {
private static final String GETORDER_KEY = "getOrder";
@GetMapping("getOrder")
public String getOrder(int id){
Entry entry = null;
try {
//參數1:資源名稱,參數三:計數,參數四:限流參數
entry = SphU.entry(GETORDER_KEY, EntryType.IN, 1, id);
// Your logic here.
} catch (BlockException ex) {
return "該用戶服務已經限流,id="+id;
}finally {
if (entry != null) {
entry.exit();
}
}
return GETORDER_KEY+",id="+id;
}
}
4. 基於熱點參數控制檯實現限流
java代碼:
@RestController
public class IndexController {
private static final String GETORDER_KEY = "getOrder";
@GetMapping("getOrder")
@SentinelResource(GETORDER_KEY)
public String getOrder(int id) {
return GETORDER_KEY + ".id=" + id;
}
}
application.yml
server:
port: 8080
spring:
cloud:
sentinel:
transport:
port: 8719
dashboard: 192.168.75.137:8718
eager: true
Sentinel控制檯中添加熱點參數限流規則:
四、Sentinel實現API熔斷降級
4.1 基於平均響應時間
java代碼:
/***
* 基於平均響應時間
* @return
*/
@SentinelResource(value = "getERFallBack",fallback = "getException")
@GetMapping("getERFallBack")
public String getERFallBack(){
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "getERFallBack";
}
/***
* 基於平均響應時間 的降級方法
* @return
*/
public String getException(){
return "服務降級啦,當前服務器請求次數過多,請稍後重試!";
}
Sentinel控制檯:
每秒平均響應時間超過閾值200ms,走服務降級,並且5秒內無法訪問服務,繼續走服務降級方法。
4.2 基於異常比例
java代碼:
/***
* 基於異常比例
* @param i
* @return
*/
@SentinelResource(value = "getErroPer",fallback = "getException")
@GetMapping("/getErroPer")
public String getErroPer(int i){
int j=1/i;
return "getErroPer";
}
/***
* 參數必須與被降級的方法中的參數一致,才能實現降級
* 異常比例和異常次數 的降級方法
* @param i
* @return
*/
public String getException(int i){
return "服務降級啦,請稍後重試!";
}
Sentinel控制檯:
4.3 基於異常次數
java代碼:
/***
* 基於異常次數
* @param i
* @return
*/
@SentinelResource(value = "getErroCout",fallback = "getException")
@GetMapping("/getErroCout")
public String getErroCout(int i){
int j=1/i;
return "getErroCout";
}
/***
* 參數必須與被降級的方法中的參數一致,才能實現降級
* 異常比例和異常次數 的降級方法
* @param i
* @return
*/
public String getException(int i){
return "服務降級啦,請稍後重試!";
}
Sentinel控制檯:
五、RestTemplate 支持
Spring Cloud Alibaba Sentinel 支持對 RestTemplate
的服務調用使用 Sentinel 進行保護,在構造 RestTemplate
bean的時候需要加上 @SentinelRestTemplate
註解。
@Bean
@SentinelRestTemplate(blockHandler = "handleException", blockHandlerClass = ExceptionUtil.class)
public RestTemplate restTemplate() {
return new RestTemplate();
}
@SentinelRestTemplate
註解的屬性支持限流(blockHandler
, blockHandlerClass
)和降級(fallback
, fallbackClass
)的處理。
其中 blockHandler
或 fallback
屬性對應的方法必須是對應 blockHandlerClass
或 fallbackClass
屬性中的靜態方法。
該方法的參數跟返回值跟 org.springframework.http.client.ClientHttpRequestInterceptor#interceptor
方法一致,其中參數多出了一個 BlockException
參數用於獲取 Sentinel 捕獲的異常。
比如上述 @SentinelRestTemplate
註解中 ExceptionUtil
的 handleException
屬性對應的方法聲明如下:
public class ExceptionUtil {
public static ClientHttpResponse handleException(HttpRequest request, byte[] body, ClientHttpRequestExecution execution, BlockException exception) {
...
}
}
NOTE: 應用啓動的時候會檢查 @SentinelRestTemplate
註解對應的限流或降級方法是否存在,如不存在會拋出異常
@SentinelRestTemplate
註解的限流(blockHandler
, blockHandlerClass
)和降級(fallback
, fallbackClass
)屬性不強制填寫。
當使用 RestTemplate
調用被 Sentinel 熔斷後,會返回 RestTemplate request block by sentinel
信息,或者也可以編寫對應的方法自行處理返回信息。這裏提供了 SentinelClientHttpResponse
用於構造返回信息。
Sentinel RestTemplate 限流的資源規則提供兩種粒度:
-
httpmethod:schema://host:port/path
:協議、主機、端口和路徑 -
httpmethod:schema://host:port
:協議、主機和端口
NOTE: 以 https://www.taobao.com/test
這個 url 並使用 GET 方法爲例。對應的資源名有兩種粒度,分別是 GET:https://www.taobao.com
以及 GET:https://www.taobao.com/test
六、動態數據源支持(數據持久化)
Sentinel中提供四種數據源實現數據持久化:File、Nacos、Zookeeper、Apolle
配置案例:
spring.cloud.sentinel.datasource.ds1.file.file=classpath: degraderule.json
spring.cloud.sentinel.datasource.ds1.file.rule-type=flow
#spring.cloud.sentinel.datasource.ds1.file.file=classpath: flowrule.json
#spring.cloud.sentinel.datasource.ds1.file.data-type=custom
#spring.cloud.sentinel.datasource.ds1.file.converter-class=org.springframework.cloud.alibaba.cloud.examples.JsonFlowRuleListConverter
#spring.cloud.sentinel.datasource.ds1.file.rule-type=flow
spring.cloud.sentinel.datasource.ds2.nacos.server-addr=localhost:8848
spring.cloud.sentinel.datasource.ds2.nacos.data-id=sentinel
spring.cloud.sentinel.datasource.ds2.nacos.group-id=DEFAULT_GROUP
spring.cloud.sentinel.datasource.ds2.nacos.data-type=json
spring.cloud.sentinel.datasource.ds2.nacos.rule-type=degrade
spring.cloud.sentinel.datasource.ds3.zk.path = /Sentinel-Demo/SYSTEM-CODE-DEMO-FLOW
spring.cloud.sentinel.datasource.ds3.zk.server-addr = localhost:2181
spring.cloud.sentinel.datasource.ds3.zk.rule-type=authority
spring.cloud.sentinel.datasource.ds4.apollo.namespace-name = application
spring.cloud.sentinel.datasource.ds4.apollo.flow-rules-key = sentinel
spring.cloud.sentinel.datasource.ds4.apollo.default-flow-rule-value = test
spring.cloud.sentinel.datasource.ds4.apollo.rule-type=param-flow
每種數據源都有兩個共同的配置項: data-type
、 converter-class
以及 rule-type
。
data-type
:表示數據類型,Sentinel 默認提供兩種內置的值,分別是 json
和 xml
(默認是json)。 若不想使用這兩種,可以添加 custom
自定義數據類型,再配置 converter-class
配置項,該配置項需要寫類的全路徑名(比如 spring.cloud.sentinel.datasource.ds1.file.converter-class=org.springframework.cloud.alibaba.cloud.examples.JsonFlowRuleListConverter
)。
rule-type
:表示數據規則,(flow
,degrade
,authority
,system
, param-flow
, gw-flow
, gw-api-group
)。
6.1 基於Nacos實現數據持久化
添加maven依賴:
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
<version>1.5.2</version>
</dependency>
application.yml
server:
port: 8080
spring:
cloud:
sentinel:
transport:
port: 8719
dashboard: 192.168.75.137:8718
eager: true
datasource:
ds:
nacos:
### nacos連接地址
server-addr: 192.168.75.137:8848
## nacos連接的分組
group-id: DEFAULT_GROUP
###路由存儲規則
rule-type: flow
### 讀取配置文件的 data-id
data-id: Sentinel-03
### 讀取培訓文件類型爲json
data-type: json
log:
dir: D:\Code\study\Java_Learning\01 Sentinel\03 基於Nacos實現數據持久化\logs\
application:
name: Sentinel-03
java代碼:
@SentinelResource(value = "getNacosInfo",blockHandler = "getQpsException")
@GetMapping("/getNacosInfo")
public String getNacosInfo(){
return "getNacosInfo";
}
public String getQpsException(){
return "該接口已被限流,請稍後再試";
}
Nacos控制檯:
resource:資源名,即限流規則的作用對象
limitApp:流控針對的調用來源,若爲 default 則不區分調用來源
grade:限流閾值類型(QPS 或併發線程數);0代表根據併發數量來限流,1代表根據QPS來進行流量控制
count:限流閾值
strategy:調用關係限流策略
七、網關限流
https://github.com/alibaba/Sentinel/wiki/%E7%BD%91%E5%85%B3%E9%99%90%E6%B5%81 [參考 Sentinel 網關限流]
注:github源碼