一、Sentinel Dashboard通信原理
1、 sentinel-transport-simple-http会在客户端主机创建新端口8719;
2、 当端口占用时端口逐次+1,直到可用;
3、 第一次启动sentinel-transport-simple-http会向Dashboard注册;
4、 、STSH默认间隔10秒钟向Dashboard发送心跳包通知健康状态;
5、 http://客户端IP:8719/api对Dashboard暴露API接口供其调用,包括获取监控数据/设置规则/查询配置信息等;
6、 Dashboard与STSH间采用REST风格通信;
注:
因涉及开辟新端口,所以不要忘记在客户端防火墙放行8719/8720等端口
微服务暴露给Sentinel Dashboard的API接口列表
[
{
url: "/cnode",
desc: "get clusterNode metrics by id, request param: id={resourceName}"
},
{
url: "/setParamFlowRules",
desc: "Set parameter flow rules, while previous rules will be replaced."
},
{
url: "/origin",
desc: "get origin clusterNode by id, request param: id={resourceName}"
},
{
url: "/cluster/server/flowRules",
desc: "get cluster flow rules"
},
{
url: "/cluster/server/modifyFlowConfig",
desc: "modify cluster server flow config"
},
{
url: "/cluster/server/modifyTransportConfig",
desc: "modify cluster server transport config"
},
{
url: "/basicInfo",
desc: "get sentinel config info"
},
{
url: "/getClusterMode",
desc: "get cluster mode status"
},
{
url: "/cluster/client/modifyConfig",
desc: "modify cluster client config"
},
{
url: "/setClusterMode",
desc: "set cluster mode, accept param: mode={0|1} 0:client mode 1:server mode"
},
{
url: "/getRules",
desc: "get all active rules by type, request param: type={ruleType}"
},
{
url: "/api",
desc: "get all available command handlers"
},
{
url: "/setRules",
desc: "modify the rules, accept param: type={ruleType}&data={ruleJson}"
},
{
url: "/cluster/server/modifyNamespaceSet",
desc: "modify server namespace set"
},
{
url: "/cluster/client/fetchConfig",
desc: "get cluster client config"
},
{
url: "/cluster/server/paramRules",
desc: "get cluster server param flow rules"
},
{
url: "/tree",
desc: "get metrics in tree mode, use id to specify detailed tree root"
},
{
url: "/cluster/server/fetchConfig",
desc: "get cluster server config"
},
{
url: "/version",
desc: "get sentinel version"
},
{
url: "/clusterNode",
desc: "get all clusterNode VO, use type=notZero to ignore those nodes with totalRequest <=0"
},
{
url: "/jsonTree",
desc: "get tree node VO start from root node"
},
{
url: "/getParamFlowRules",
desc: "Get all parameter flow rules"
},
{
url: "/cluster/server/modifyParamRules",
desc: "modify cluster param flow rules"
},
{
url: "/metric",
desc: "get and aggregate metrics, accept param: startTime={startTime}&endTime={endTime}&maxLines={maxLines}&identify={resourceName}"
},
{
url: "/systemStatus",
desc: "get system status"
},
{
url: "/cluster/server/modifyFlowRules",
desc: "modify cluster flow rules"
},
{
url: "/cluster/server/metricList",
desc: "get cluster server metrics"
},
{
url: "/getSwitch",
desc: "get sentinel switch status"
},
{
url: "/setSwitch",
desc: "set sentinel switch, accept param: value={true|false}"
},
{
url: "/clusterNodeById",
desc: "get clusterNode VO by id, request param: id={resourceName}"
},
{
url: "/cluster/server/info",
desc: "get cluster server info"
}
]
Sentinel Dashboard控制台配置项,可以修改
二、Sentinel三种保护应用方式
1、拦截所有controller的请求url路径
Sentinel为springboot程序提供了一个starter依赖,由于sentinel starter依赖默认情况下就会为所有的HTTP服务提供限流埋点,所以在springboot 中的Controller都可以受到Sentinel的保护;
只需为应用添加 spring-cloud-starter-alibaba-sentinel依赖,所有的HTTP接口都能获得Sentinel保护,还需要为Sentinel配置保护的规则;
底层通过一个拦截器对请求url进行拦截
com.alibaba.csp.sentinel.adapter.spring.webmvc.SentinelWebInterceptor
通过如下配置关闭对微服务的保护
#关闭sentinel对controller的url的保护
spring.cloud.sentinel.filter.enabled=false
2、通过代码方式保护应用
@GetMapping("/test3/{app}")
public String test3(@PathVariable("app") String app) {
System.out.println("/test3/{app} --> " + app);
//跟踪
ContextUtil.enter("test3");
Entry entry = null;
try {
entry = SphU.entry("test3");
//受sentinel保护的代码 start
int a = 10 / 0;
return restTemplate.getForObject("http://springcloud-alibaba-1-nacos-discovery-provider/test", String.class);
//受sentinel保护的代码 end
} catch (BlockException e) {
e.printStackTrace();
不同的异常返回不同的提示语(手动写上服务降级的代码)
if (e instanceof FlowException) {
return "限流了...";
} else if (e instanceof DegradeException) {
return "熔断降级了...";
} else if (e instanceof ParamFlowException) {
return "热点参数限流了...";
} else if (e instanceof SystemBlockException) {
return "触发系统保护规则...";
} else if (e instanceof AuthorityException) {
return "授权规则不通过...";
}
return "熔断了.....";
} catch (ArithmeticException e) {
//对 int a = 10 / 0; 异常的监控(跟踪统计到 BlockException 中)
Tracer.trace(e);
return "除数不能为0";
} finally {
//退出关闭(类似流关闭)
if (entry != null) {
entry.exit();
}
ContextUtil.exit();
}
}
3、通过@SentinelResource(value = "app")注解保护应用
属性 | 说明 | 必填与否 | 使用要求 |
---|---|---|---|
value | 用于指定资源的名称 | 必填 | - |
entryType | entry 类型 | 可选项(默认为 EntryType.OUT) | - |
blockHandler | 服务限流后会抛出 BlockException 异常,而 blockHandler 则是用来指定一个函数来处理 BlockException 异常的。 简单点说,该属性用于指定服务限流后的后续处理逻辑。 |
可选项 |
|
blockHandlerClass | 若 blockHandler 函数与原方法不在同一个类中,则需要使用该属性指定 blockHandler 函数所在的类。 | 可选项 |
|
fallback | 用于在抛出异常(包括 BlockException)时,提供 fallback 处理逻辑。 fallback 函数可以针对所有类型的异常(除了 exceptionsToIgnore 里面排除掉的异常类型)进行处理。 |
可选项 |
|
fallbackClass | 若 fallback 函数与原方法不在同一个类中,则需要使用该属性指定 blockHandler 函数所在的类。 | 可选项 |
|
defaultFallback | 默认的 fallback 函数名称,通常用于通用的 fallback 逻辑(即可以用于很多服务或方法)。 默认 fallback 函数可以针对所以类型的异常(除了 exceptionsToIgnore 里面排除掉的异常类型)进行处理。 |
可选项 |
|
exceptionsToIgnore | 用于指定哪些异常被排除掉,不会计入异常统计中,也不会进入 fallback 逻辑中,而是会原样抛出。 | 可选项 | - |
三、Sentinel对 RestTemplate 流控熔断
在application.properties配置文件中,开启sentinel对resttemplate的支持
#true开启sentinel对resttemplate的支持,false则关闭
resttemplate.sentinel.enabled=true
@Configuration
public class MyRibbonConfig {
/*
在使用springcloud ribbon客户端负载均衡的时候,可以给RestTemplate bean 加一个@LoadBalanced注解,就能让这个RestTemplate在请求时拥有客户端负载均衡的能力
这个@LoadBalanced注解是来自cloud包下的一个注解
*/
// @SentinelRestTemplate(blockHandler="blockA", blockHandlerClass= MyBlockHandlerClass.class) //限流
@SentinelRestTemplate(fallback="fallbackA", fallbackClass = MyBlockHandlerClass.class) // 降级
@Bean
@LoadBalanced //与 Ribbon 集成,并开启负载均衡功能
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
public class MyBlockHandlerClass {
/**
* 该方法一定要是static方法
*/
public static String block(String a, String b, BlockException e) {
System.out.println("Block Handler--> " + a + "--" + b);
return "Block Handler.";
}
/**
* 限流后处理方法
*/
public static SentinelClientHttpResponse blockA(HttpRequest request,
byte[] body, ClientHttpRequestExecution execution, BlockException ex) {
System.err.println("block: " + ex.getClass().getCanonicalName());
return new SentinelClientHttpResponse("custom block info");
}
/**
* 熔断后处理的方法
*/
public static SentinelClientHttpResponse fallbackA(HttpRequest request,
byte[] body, ClientHttpRequestExecution execution, BlockException ex) {
System.err.println("fallback: " + ex.getClass().getCanonicalName());
return new SentinelClientHttpResponse("custom fallback info");
}
}
/**
* 自定义限流降级错误信息
*/
@Slf4j
@Component
public class MyBlockExceptionHandler implements BlockExceptionHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception {
log.info("UrlBlockHandler.....................................");
RestObject restObject = null;
// 不同的异常返回不同的提示语
if (e instanceof FlowException) {
restObject = RestObject.builder().statusCode(100).statusMessage("接口限流了").build();
} else if (e instanceof DegradeException) {
restObject = RestObject.builder().statusCode(101).statusMessage("服务降级了").build();
} else if (e instanceof ParamFlowException) {
restObject = RestObject.builder().statusCode(102).statusMessage("热点参数限流了").build();
} else if (e instanceof SystemBlockException) {
restObject = RestObject.builder().statusCode(103).statusMessage("触发系统保护规则").build();
} else if (e instanceof AuthorityException) {
restObject = RestObject.builder().statusCode(104).statusMessage("授权规则不通过").build();
}
//返回json数据
response.setStatus(500);
response.setCharacterEncoding("utf-8");
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
//springmvc 的一个json转换类 (jackson)
new ObjectMapper().writeValue(response.getWriter(), restObject);
//跳转
//request.getRequestDispatcher("/index.jsp").forward(request, response);
//重定向
//response.sendRedirect("http://www.baidu.com");
}
}
四、Sentinel对 OpenFeign 流控熔断
在application.properties配置文件中,开启sentinel对feign的支持
#true开启sentinel对feign的支持,false则关闭
feign.sentinel.enabled=true
@FeignClient(name = "springcloud-alibaba-1-nacos-discovery-provider",
/*fallback = EchoFeignServiceFallback.class,*/
fallbackFactory = EchoFeignServiceFallbackFactory.class,
configuration = FeignConfiguration.class)
public interface EchoFeignService {
@GetMapping("/notFound")
String notFound();
}
@Component
public class EchoFeignServiceFallback implements EchoFeignService {
@Override
public String notFound() {
return "notFound fallback";
}
}
public class EchoFeignServiceFallbackFactory implements FallbackFactory<EchoFeignService> {
@Override
public EchoFeignService create(Throwable throwable) {
return new EchoFeignService() {
@Override
public String notFound() {
return "default feign invoke notFound fallbackFactory 999" + throwable.getMessage();
}
};
}
}
public class FeignConfiguration {
@Bean
public EchoFeignServiceFallbackFactory echoFeignServiceFallbackFactory() {
return new EchoFeignServiceFallbackFactory();
}
}