文章目錄
- 7.熱點參數規則限流
- 8.系統規則限流
- 9.授權規則限流
- 10.sentinel控制檯如何監控微服務數據?
- 11.常用的自定義參數
- 12.Feign,RestTemplate整合Sentinel
- 13.Sentinel 擴展
- 13.1 Sentinel規則錯誤提示優化
- 13.1.1 普通spring mvc資源的錯誤提示優化
- 13.1.2 使用@SentinelResource註解資源的錯誤提示優化
- 13.1.3 Feign 通訊資源的錯誤提示優化
- 13.1.4 RestTemplate 通訊資源的錯誤提示優化
- 13.2 Sentinel 實現區分來源
- 13.3 Sentinel 對Restful Url的支持
- 14.Sentinel 規則持久化
特別聲明:整理自慕課網大目師兄的微服務視頻,鏈接:https://coding.imooc.com/learn/list/358.html
前情提要:接續服務容錯 Sentinel 上篇
7.熱點參數規則限流
熱點即經常訪問的數據
商品 ID 爲參數,統計一段時間內最常購買的商品 ID 並進行限制
用戶 ID 爲參數,針對一段時間內頻繁訪問的用戶 ID 進行限制
7.1 針對第二個傳參限流
規則:如果第二個參數有值,QPS達到閾值,限流;如果第二個參數沒傳值,不限流
- 準備代碼
@SentinelResource註解的作用是把參數納入統計
@RestController
@Slf4j
@RequestMapping("/hot")
public class HotParamController {
@GetMapping("basic")
// 把方法的參數納入統計
@SentinelResource("basic")
public String basic(String a,String b){
log.info("basic......");
return a + ":" + b;
}
- 配置規則
參數索引:參數的位置,從0開始。參數b是第二個參數,所以這裏配置1
- 測試
不傳參數b,瘋狂刷新:http://localhost:8081/hot/basic?a=1,不會被限流
帶有參數b,瘋狂刷新:http://localhost:8081/hot/basic?a=1&b= 或者 http://localhost:8081/hot/basic?a=1&b=hello,被限流
7.2 針對參數具體值的限流
熱點規則 ------> 點擊編輯(新增的時候是沒有的)
- 點開高級,配置規則
下面的規則含義:參數b的閾值是1,但有例外情況,如果b=hello時,閾值是1000;如果b=hai,閾值是2
- 測試
瘋狂刷新 http://localhost:8081/hot/basic?a=1&b=,b=""或者其它值,閾值是1,很容易被限流
瘋狂刷新 http://localhost:8081/hot/basic?a=1&b=hello,b=“hello”,閾值是1000,憑手速達不到QPS 1000,不會限流
瘋狂刷新 http://localhost:8081/hot/basic?a=1&b=hai,b=“hai”,閾值是2,容易限流
8.系統規則限流
- 應用級別的入口流量進行控制,從單臺機器的總體 Load、RT、入口 QPS 和線程數四個維度監控應用數據,讓系統儘可能跑在最大吞吐量的同時保證系統整體的穩定性
- 系統保護規則是應用整體維度的,而不是資源維度的,並且僅對入口流量生效
8.1 四種閾值類型
8.1.1 Load
8.1.1.1 釋義
- 當系統load1(一分鐘的負載)超過閾值,且系統當前的併發線程數超過系統容量時纔會觸發系統保護
- 系統容量:系統的 maxQps * minRt 計算得出
maxQps:秒級統計最大的QP
minRt:秒級統計的最小響應時間 - 閾值:一般是CPU cores * 2.5
- 僅對Linux/Unix-like機器生效
8.1.1.2 查看linux系統的load
uptime
紅框裏的三個數分別是1分鐘內的平均負載,5分鐘內的平均負載,15分鐘內的平均負載
8.1.2 RT
所有入口流量的平均RT
8.1.3 線程數
所有入口流量的併發線程數
8.1.4 QPS
所有入口流量的QPS
9.授權規則限流
針對應用級別的黑白名單,參數的意思也都很好理解
10.sentinel控制檯如何監控微服務數據?
- 微服務把自己註冊到sentinel控制檯,使用心跳機制通訊
- sentinel的機器列表
- 通訊的api有哪些?
http://192.168.56.1:8720/api
- 服務端查看sentinel詳情
http://localhost:8081/actuator/sentinel
11.常用的自定義參數
11.1 服務方常用的自定義參數
11.2 sentinel控制檯常用的自定義參數
- 自定義端口示例
java -jar -DServer.port=8090 sentinel-dashboard-1.6.3.jar
端口已改成8090
12.Feign,RestTemplate整合Sentinel
Sentinel不僅可以控制本服務的資源,還可以限流Feign和RestTemplate對外的請求
12.1 RestTemplate整合Sentinel
- 添加註解@SentinelRestTemplate
@Configuration
public class RestTemplateConfig {
@Bean
@LoadBalanced
@SentinelRestTemplate
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
- sentinel控制檯添加限流規則,簇點鏈路這邊可以看到restTemplate發送的請求了,添加註解之前是沒有的
- 關閉註解@SentinelRestTemplate,默認是開啓的
resttemplate:
sentinel:
# 關閉 @SentinelRestTemplate 作用
enabled: false
12.2 Feign整合Sentinel
- 寫配置
feign:
sentinel:
# IDE不會提示,但是有效
enabled: true
- 使用feign,sentinel這邊已經可以看到 GET:http://user-center/reciteHis/testAno 資源了
如果spring-cloud-alibaba是0.9.0版本並且你的Feign是用繼承模式實現的,那麼會報 NullPointerException 錯誤,把spring-cloud-alibaba升級到2.1.0.RELEASE就沒有了,我踩了這個坑…
13.Sentinel 擴展
13.1 Sentinel規則錯誤提示優化
之前,我測試的時候,限流規則,降級規則,熱點規則等待,返回的都是一句錯誤消息,或者乾脆報異常,無法區別到底違反了哪個規則?這一節的擴展可以優化這個問題
13.1.1 普通spring mvc資源的錯誤提示優化
- 寫 ContentUrlBlockHandler 實現 UrlBlockHandler,並加上@Component
package com.zengchen.content.sentinel;
import com.alibaba.csp.sentinel.adapter.servlet.callback.UrlBlockHandler;
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.fasterxml.jackson.databind.ObjectMapper;
import com.zengchen.content.common.ResponseVO;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Component
public class ContentUrlBlockHandler implements UrlBlockHandler {
@Override
public void blocked(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, BlockException e) throws IOException {
ResponseVO sentinelErrorMsg = null;
// 根據異常類型區分違反哪種規則
if(e instanceof FlowException){// 限流異常
sentinelErrorMsg = ResponseVO.builder()
.code(100)
.msg("被限流規則阻擋")
.build();
}else if(e instanceof DegradeException){ // 降級異常
sentinelErrorMsg = ResponseVO.builder()
.code(101)
.msg("被降級規則阻擋")
.build();
}else if(e instanceof ParamFlowException){ // 熱點參數異常
sentinelErrorMsg = ResponseVO.builder()
.code(102)
.msg("被熱點參數規則阻擋")
.build();
}else if(e instanceof SystemBlockException){ // 系統規則異常
sentinelErrorMsg = ResponseVO.builder()
.code(103)
.msg("被系統規則阻擋")
.build();
}else if(e instanceof AuthorityException){ // 認證異常
sentinelErrorMsg = ResponseVO.builder()
.code(104)
.msg("被授權規則阻擋")
.build();
}else{
sentinelErrorMsg = ResponseVO.builder()
.code(999)
.msg("Unknown")
.build();
}
httpServletResponse.setStatus(500);
httpServletResponse.setCharacterEncoding("utf-8");
httpServletResponse.setHeader("Content-Type","application/json;charset=utf-8");
httpServletResponse.setContentType("application/json;charset=utf-8");
new ObjectMapper().writeValue(httpServletResponse.getWriter(),sentinelErrorMsg);
}
}
ResponseVO:
package com.zengchen.content.common;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* http請求返回的最外層對象
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ResponseVO<T> {
/**
* 錯誤碼
*/
private Integer code;
/**
* 提示信息
*/
private String msg;
/**
* 具體內容
*/
private T data;
public static ResponseVO success(Object data){
ResponseVO responseVO = new ResponseVO();
responseVO.setCode(0);
responseVO.setData(data);
responseVO.setMsg("OK");
return responseVO;
}
public static ResponseVO fail(Integer code,String msg){
ResponseVO responseVO = new ResponseVO();
responseVO.setCode(code);
responseVO.setMsg(msg);
return responseVO;
}
}
- 測試
限流規則
降級規則
熱點參數規則
失敗了,程序並沒有走進ContentUrlBlockHandler,因爲熱點參數測試方法上添加了@SentinelResource註解,有這個註解就不能使用ContentUrlBlockHandler來做錯誤頁優化,下小節會講
系統規則
授權規則
13.1.2 使用@SentinelResource註解資源的錯誤提示優化
主要使用@SentinelResource註解的blockHandler,blockHandlerClass,fallback,fallbackClass四個屬性
13.1.2.1 屬性詳解
- blockHandler
值是方法名,資源受到規則限制,會調用此方法
方法要求:訪問控制 public,返回值與資源方法一直,參數是資源方法參數 + BlockException e - blockHandlerClass
當blockHandler方法所在位置於資源方法不在一個類時,用這個屬性指定 - fallback
值是方法名,資源方法發生異常時,會調用此方法
方法要求:訪問控制 public,返回值與資源方法一直,參數是資源方法參數 + Throwable e(版本1.6.0之和纔有Throwable參數) - fallbackClass
當fallback方法所在位置於資源方法不在一個類時,用這個屬性指定
13.1.2.2 示例代碼
- 資源方法:
@RestController
@Slf4j
public class SentinelResourceController {
@GetMapping("/test-blockHandler")
@SentinelResource(value = "blockHandler",
blockHandler = "block",blockHandlerClass = SentinelResourceBlockHandler.class ,
fallback = "fallBack",fallbackClass = SentinelResourceFallbackHandler.class)
public String testBlockHandler(String a,String b) throws InterruptedException {
throw new IllegalArgumentException();
}
}
- SentinelResourceBlockHandler
@Slf4j
public class SentinelResourceBlockHandler {
public static String block(String a,String b, BlockException e){
String errorMsg = "Unknown";
// 根據異常類型區分違反哪種規則
if(e instanceof FlowException){// 限流異常
errorMsg = "被限流規則阻擋";
}else if(e instanceof DegradeException){ // 降級異常
errorMsg = "被降級規則阻擋";
}else if(e instanceof ParamFlowException){ // 熱點參數異常
errorMsg = "被熱點參數規則阻擋";
}else if(e instanceof SystemBlockException){ // 系統規則異常
errorMsg = "被系統規則阻擋";
}else if(e instanceof AuthorityException){ // 認證異常
errorMsg = "被授權規則阻擋";
}
log.warn("block method,{}",errorMsg ,e);
return errorMsg;
}
}
- SentinelResourceFallbackHandler
@Slf4j
public class SentinelResourceFallbackHandler {
public static String fallBack(String a,String b,Throwable e){
log.warn("fallBack method",e);
return "出現異常了";
}
}
13.1.3 Feign 通訊資源的錯誤提示優化
還記得@FeignClient註解嗎?也有fallback,fallbackFactory屬性
只能配置一個總的,因爲都是一個feignClient,入口統一了
- 代碼
SentinelFeignFallbackFactory
@Component
@Slf4j
public class SentinelFeignFallbackFactory implements FallbackFactory<UserCenterFeignClient> {
@Override
public UserCenterFeignClient create(Throwable throwable) {
return new UserCenterFeignClient() {
@Override
public Page<ReciteHisOT> memberRctHisAno() {
log.warn("memberRctHisAno FallbackFactory");
return null;
}
};
}
}
UserCenterFeignClient的fallbackFactory
@FeignClient(name = "user-center", fallbackFactory = SentinelFeignFallbackFactory.class)
public interface UserCenterFeignClient
extends UserCenterFeignClientService
{
/**
* FeignClient的name + GetMapping的value
* 相當於 http://user-center/reciteHis/testAno
* 和RestTemplate裏寫的url一模一樣
* @return Page
*/
// @GetMapping(value = "/reciteHis/testAno")
// Page<ReciteHisOT> memberRctHisAno();
}
配置個限流規則,觸發規則時,進入fallbackFactory
13.1.4 RestTemplate 通訊資源的錯誤提示優化
還記得RestTemplate怎麼和sentinel整合的嗎?也是一個註解@SentinelRestTemplate,這個註解和@SentinelResource一樣,有blockHandler,blockHandlerClass,fallback,fallbackClass四個屬性
也只能配置一個總的,因爲都是一個入口
不寫代碼了,反正我用Feign
13.2 Sentinel 實現區分來源
授權規則的黑白名單需要來源,流控規則也有針對來源的屬性,但是這個來源是怎麼來的呢?sentinel提供了RequestOriginParser接口,寫個類實現這個接口,唯一方法的返回值就是來源
- 代碼
@Component
public class ContentRequestOriginParser implements RequestOriginParser {
@Override
public String parseOrigin(HttpServletRequest httpServletRequest) {
// String origin = httpServletRequest.getHeader("origin");
String origin = httpServletRequest.getParameter("origin"); // 放在參數中方便測試
if(StringUtils.isBlank(origin)){
throw new IllegalArgumentException("origin is blank!");
}
return origin;
}
}
- 測試授權規則
13.3 Sentinel 對Restful Url的支持
問題說明:如下圖,僅僅id不一樣,這規則怎麼配置?
sentinel的UrlCleaner會幫助我們解決這個問題
- restful接口DownController
@GetMapping("restful/{id}")
public String restful(@PathVariable Integer id) {
log.info("restful start with {}", id);
return "" + id;
}
@GetMapping("restful/{id}/test")
public String restfulTest(@PathVariable Integer id) {
log.info("restfulTest start with {}", id);
return "test " + id;
}
- UrlCleaner
@Component
@Slf4j
public class ContentUrlCleaner implements UrlCleaner {
@Override
public String clean(String path) {
log.info("ContentUrlCleaner start with {}", path);
String[] paths = path.split("/");
path = Arrays.stream(paths).map(string -> {
if (NumberUtils.isNumber(string)) {
return "{number}";
}
return string;
}).reduce((a, b) -> {
return a + "/" + b;
}).orElse("");
log.info("ContentUrlCleaner end with {}", path);
return path;
}
}
- sentinel控制檯,簇點鏈路,url已經改變
14.Sentinel 規則持久化
現在sentinel有個致命缺陷,服務重啓或者sentinel重啓,配置的規則都會丟失,如果是生產上,這還了得?這種事絕對不能發生。下面介紹三種解決辦法
14.1 拉模式
參考大目老師手記:https://www.imooc.com/article/289402,生產環境不建議用這個,因爲使用之後後患不小
14.2 推模式
- 原理
- 實現,工程浩大
參考大目老師手記 https://www.imooc.com/article/289464 - 懶人包
大目老師提供了懶人包: https://github.com/eacdy/Sentinel-Dashboard-Nacos/releases,這個是供學習用的,不要直接用於生產
14.3 阿里 ahas
免費的可以接入5個節點,專業的收費
開通地址:https://ahas.console.aliyun.com
開通說明:https://help.aliyun.com/document_detail/90323.html
14.3.1 加依賴
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>spring-boot-starter-ahas-sentinel-client</artifactId>
<version>1.3.5</version>
</dependency>
14.3.2 添加埋點
springcloud-alibaba已經幫我們做過了,省略
14.3.3 配置啓動參數
這裏選擇公網,纔有license
我的ahas.license就不暴露出來了
註釋掉
# transport:
# # 指定 sentinel 控制檯地址
# dashboard: localhost:8080
14.3.4 重啓應用
隨便訪問一個接口,比如:http://localhost:8081/down/restful/2/test,再刷新阿里的控制檯,就可以看到了
,