携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第11天,点击查看活动详情
Spring Cloud系列文章
Sentinel概述
Sentinel 是面向分布式、多语言异构化服务架构的流量治理组件,主要以流量为切入点,从流量控制、流量路由、熔断降级、系统自适应保护等多个维度来帮助用户保障微服务的稳定性。
—— 引自Sentinel官网
Sentinel支持两种资源维度的限流:
- Route维度
- 自定义API维度
Route维度限流
- 引入Maven依赖
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-spring-cloud-gateway-adapter</artifactId>
</dependency>
- 网关配置类
@Configuration
@AllArgsConstructor
public class GatewayConfig {
private final List<ViewResolver> viewResolvers;
private final ServerCodecConfigurer serverCodecConfigurer;
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public GlobalFilter sentinelGatewayFilter() {
return new SentinelGatewayFilter();
}
// 配置限流异常处理器,即触发限流后的默认处理类
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public CustomSentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {
return new CustomSentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);
}
@PostConstruct
public void doInit() {
initGatewayRules();
}
// 初始化限流规则
private void initGatewayRules() {
Set<GatewayFlowRule> rules = new HashSet<>();
GatewayFlowRule gatewayFlowRule = new GatewayFlowRule("jasmine-auth");
gatewayFlowRule.setCount(1D);
gatewayFlowRule.setIntervalSec(5L);
rules.add(gatewayFlowRule);
GatewayRuleManager.loadRules(rules);
}
}
- 自定义全局限流异常处理器
在实际的应用中(笔者接触的项目大体是前后端分离的),我们通常以JSON的格式返回数据。我们自定义了一个异常处理器,当触发限流时返回JSON格式的异常数据。
public class CustomSentinelGatewayBlockExceptionHandler implements WebExceptionHandler {
private final List<ViewResolver> viewResolvers;
List<HttpMessageWriter<?>> messageWriters;
public CustomSentinelGatewayBlockExceptionHandler(List<ViewResolver> viewResolvers, ServerCodecConfigurer serverCodecConfigurer) {
this.viewResolvers = viewResolvers;
this.messageWriters = serverCodecConfigurer.getWriters();
}
private Mono<Void> writeResponse(ServerResponse response, ServerWebExchange exchange) {
ServerHttpResponse serverHttpResponse = exchange.getResponse();
serverHttpResponse.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
Map<String, Object> map = new HashMap<>();
map.put("code", 500);
map.put("msg", "访问人数过多!");
DataBuffer dataBuffer = serverHttpResponse.bufferFactory().wrap(new Gson().toJson(map).getBytes(StandardCharsets.UTF_8));
return serverHttpResponse.writeWith(Mono.just(dataBuffer));
}
// 省略无关代码
}
自定义全局限流异常处理器的代码是直接从SentinelGatewayBlockExceptionHandler中复制过来,我们只修改writeResponse方法,该方法的作用是将限流的异常信息写回客户端。
配置类的主要功能如下:
- 注入一个全局限流控制器
- 注入自定义全局限流异常处理器
- 初始化限流规则
网关限流规则(GatewayFlowRule)中提供了如下属性:
- 资源名称(resource):可以是网关中的route名称或者用户自定义的API分组名称;
- 资源模型(resourceMode):
- 限流指标维度(grade):同限流规则的
grade字段; - 限流阈值(count)
- 统计时间窗口(intervalSec):单位是秒,默认1秒;
- 流量整形的控制效果(controlBehavior)
- 应对突发请求时额外允许的请求数目(burst)
- 匀速排队模式下的最长排队时间(maxQueueingTimeoutMs):单位是毫秒,仅在匀速排除模式下生效;
- 参数限流配置(paramItem)
- 从请求中提取参数的策略(parseStrategy),目前支持提取来源IP(PARAM_PARSE_STRATEGY_CLIENT_IP)、Host(PARAM_PARSE_STRATEGY_HOST)、任意Header (PARAM_PARSE_STRATEGY_HEADER)和任意URL参数(PARAM_PARSE_STRATEGY_URL_PARAM)四种模式。
- 若提取策略选择Header模式或URL参数模式,则需要指定对应的Header名称或URL参数名称。
- 项目配置文件
server:
port: 9010
spring:
profiles:
active: dev
application:
name: jasmine-sentinel
cloud:
nacos:
config:
file-extension: yaml
group: ${spring.profiles.active}
prefix: ${spring.application.name}
server-addr: 127.0.0.1:8848
gateway:
discovery:
locator:
enabled: true
# 是否使用service-id的小写,默认是大写
lower-case-service-id: true
routes:
- id: jasmine-auth
uri: lb://jasmine-auth
predicates:
- Path=/auth/**
关键配置说明:
- uri: 配置的lb://表示从注册中心获取服务,后面的jasmine-auth表示目标服务在注册中心上的服务名。
- 测试
最后在浏览器中访问http://127.0.0.1:9010/auth/hello,多次刷新,当触发限流规则时看到的返回结果如下所示:
{"msg":"访问人数过多!","code":500}
自定义API分组限流
自定义API分组限流可以让多个Route共用一个限流规则。
自定义Api分组限流
// 自定义Api分组限流
private void initCustomizedApis() {
Set<ApiDefinition> definitions = new HashSet<>();
ApiDefinition apiDefinition = new ApiDefinition("jasmine-customized-api")
.setPredicateItems(new HashSet<ApiPredicateItem>() {{
add(new ApiPathPredicateItem().setPattern("/auth/**"));
add(new ApiPathPredicateItem().setPattern("/log/**")
.setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX));
}});
definitions.add(apiDefinition);
GatewayApiDefinitionManager.loadApiDefinitions(definitions);
}
上述代码主要是将/auth/**和/log/**进行统一分组,并提供一个name=jasmine-customized-api,然后在初始化网关限流规则时,针对该name设置限流规则。同时,我们可以通过setMatchStrategy来设置不同path下的限流参数策略。
// 初始化限流规则
private void initGatewayRules() {
Set<GatewayFlowRule> rules = new HashSet<>();
GatewayFlowRule gatewayFlowRule = new GatewayFlowRule("jasmine-customized-api");
gatewayFlowRule.setCount(1D);
gatewayFlowRule.setIntervalSec(5L);
gatewayFlowRule.setResourceMode(SentinelGatewayConstants.RESOURCE_MODE_CUSTOM_API_NAME);
rules.add(gatewayFlowRule);
GatewayRuleManager.loadRules(rules);
}
initCustomizedApis()方法和initGatewayRules()方法一样,在初始化的时候调用。
@PostConstruct
public void doInit() {
initCustomizedApis();
initGatewayRules();
}
这样,我们在浏览器中访问http://127.0.0.1:9010/auth/hello和http://127.0.0.1:9010/log/hello,多次刷新,都会触发限流规则,然后看到的返回结果如下所示:
{"msg":"访问人数过多!","code":500}
网关流控实现原理
图片引自官网。
如果觉得文章写得还凑合,还麻烦你动动小手帮忙点个赞呗。你的支持是我更文的动力。目前在写的系列有下面几个系统:
- MyBatis源码解读
- 设计模式
- 一起学Three.js
- Spring Boot
- Spring Cloud
都是一些平时工作中用到的知识点的个人总结和学习笔记。