Spring Cloud :Gateway 集成 Sentinel (六)
目录
3. bootstrap.yml 配置 sentinel服务地址
Spring Cloud Gateway 学习专栏
1. Spring Cloud : Gateway 服务网关认识(一)
2. Spring Cloud :整合Gateway 学习 (二)
3. Spring Cloud:Gateway 路由定义定位器 RouteDefinitionLocator (三)
4. Spring Cloud : Gateway 网关过滤器 GatewayFilter(四)
5. Spring Cloud : Gateway 网关限流(五)
6. Spring Cloud:Gateway 集成 Sentinel (六)
7. Spring Cloud : Gateway Redis动态路由 (七)
8. Spring Cloud : Gateway 整合Swagger (八)
如果发现本文有错误的地方,请大家毫不吝啬,多多指教,欢迎大家评论,谢谢!
一、概述
Spring Cloud Gateway是Spring Cloud官方推出的第二代网关框架,取代Zuul网关。网关作为流量的,在微服务系统中有着非常作用,网关常见的功能有路由转发、权限校验、限流控制等作用。Sentinel是阿里开源的项目,提供了流量控制、熔断降级、系统负载保护等多个维度来保障服务之间的稳定性。(https://github.com/alibaba/Sentinel)
整体结构图如下,将原有的 Spring Cloud Gateway中集成Hystrix替换成Sentinel来进行限流、降级等功能, Hystrix和Sentinel的区别可以参考:[Hystrix和Sentinel对比][1];
总结来说:Hystrix常用的线程池隔离会造成线程上下切换的overhead比较大;Hystrix使用的信号量隔离对某个资源调用的并发数进行控制,效果不错,但是无法对慢调用进行自动降级;Sentinel通过并发线程数的流量控制提供信号量隔离的功能
二、Sentinel快速搭建
1. sentinel 下载
sentinel release下载地址
https://github.com/alibaba/Sentinel/releases
2. 命令启动
采用java -jar 命令启动 sentinel dashboard
java -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard.jar
下面一段命令是把sentinel dashboard 服务也注册到sentinel上去
-Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=sentinel-dashboard
登录参数配置:
从 Sentinel 1.6.0 起,Sentinel 控制台引入基本的登录功能,默认用户名和密码都是 sentinel。可以参考 鉴权模块文档 配置用户名和密码。
- Dsentinel.dashboard.auth.username=sentinel 用于指定控制台的登录用户名为 sentinel;
- Dsentinel.dashboard.auth.password=123456 用于指定控制台的登录密码为 123456;如果省略这两个参数,默认用户和密码均为 sentinel;
- Dserver.servlet.session.timeout=7200 用于指定 Spring Boot 服务端 session 的过期时间,如 7200 表示 7200 秒;60m 表示 60 分钟,默认为 30 分钟;
3. sentinel dashborad
输入账号 :sentinel 密码:sentinel
三、Gateway 整合 Sentinel
1. 引入 pom 依赖
<!-- sentinel提供的gataway适配器 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
</dependency>
<!--sentinel依赖包-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
2. RulesController
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinition;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.GatewayApiDefinitionManager;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayRuleManager;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.Set;
/**
* sentinel 接口控制
* @date: 2021/4/20 14:52
*/
@RestController
public class RulesController {
/**
* Api定义
* @date: 2021/4/22 10:23
* @return: java.util.Set<com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinition>
*/
@GetMapping("/api")
@SentinelResource("api")
public Set<ApiDefinition> apiRules() {
return GatewayApiDefinitionManager.getApiDefinitions();
}
/**
* 获取 Route 限流配置信息
* @date: 2021/4/22 10:22
* @return: java.util.Set<com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule>
*/
@GetMapping("/gateway")
@SentinelResource("gateway")
public Set<GatewayFlowRule> apiGateway() {
return GatewayRuleManager.getRules();
}
/**
* 流规则
* @date: 2021/4/22 10:24
* @return: java.util.List<com.alibaba.csp.sentinel.slots.block.flow.FlowRule>
*/
@GetMapping("/flow")
@SentinelResource("flow")
public List<FlowRule> apiFlow() {
return FlowRuleManager.getRules();
}
}
3. bootstrap.yml 配置 sentinel服务地址
spring:
cloud:
sentinel:
transport:
dashboard: localhost:8890
port: 8890
# 服务启动直接建立心跳连接
eager: true
4. GatewayConfiguration 配置
/**
* 网关 Sentinel 配置信息配合限流规则
* @date: 2021/4/22 10:37
* @author: Zou.LiPing
*/
@Slf4j
@Configuration
public class GatewayConfiguration {
private final List<ViewResolver> viewResolvers;
private final ServerCodecConfigurer serverCodecConfigurer;
public GatewayConfiguration(ObjectProvider<List<ViewResolver>> viewResolversProvider,
ServerCodecConfigurer serverCodecConfigurer) {
this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);
this.serverCodecConfigurer = serverCodecConfigurer;
}
/**
* 配置SentinelGatewayFilter
* @return GlobalFilter
*/
@Bean
@Order(-5)
public GlobalFilter sentinelGatewayFilter() {
return new SentinelGatewayFilter();
}
@PostConstruct
public void doInit() {
initGatewayRules();
initCustomizedApis();
initBlockExceptionHandler();
}
/**
* 初始化自定义异常
* @date: 2021/4/20 17:21
*/
private void initBlockExceptionHandler() {
BlockRequestHandler blockRequestHandler = (serverWebExchange, throwable) -> {
String value = serverWebExchange.getRequest().getPath().pathWithinApplication().value();
Result<String> failed = Result.failed(ResultCode.TOO_MANY_REQUESTS, value);
log.error("initBlockExceptionHandler.resp failed={}",failed);
return ServerResponse
.status(HttpStatus.TOO_MANY_REQUESTS)
.contentType(MediaType.APPLICATION_JSON_UTF8)
.body(BodyInserters.fromObject(failed));
};
GatewayCallbackManager.setBlockHandler(blockRequestHandler);
}
/**
* 初始化自定义API
* @date: 2021/4/20 16:46
*
*/
private void initCustomizedApis() {
/*
ApiDefinition:用户自定义的 API 定义分组,可以看做是一些 URL 匹配的组合。
比如我们可以定义一个 API 叫 user-api ,请求 path 模式为 /foo/** 和 /baz/** 的都归到 my_api 这个 API 分组下面。
限流的时候可以针对这个自定义的 API 分组维度进行限流。
*/
Set<ApiDefinition> definitions = new HashSet<>();
ApiDefinition userApi = new ApiDefinition("user-api")
.setPredicateItems(new HashSet<ApiPredicateItem>() {{
add(new ApiPathPredicateItem().setPattern("/user/**")
.setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX));
}});
ApiDefinition productApi = new ApiDefinition("product-api")
.setPredicateItems(new HashSet<ApiPredicateItem>() {{
add(new ApiPathPredicateItem().setPattern("/product/**")
.setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX));
}});
definitions.add(userApi);
definitions.add(productApi);
GatewayApiDefinitionManager.loadApiDefinitions(definitions);
}
/**
* 配置限流规则
*/
private void initGatewayRules() {
Set<GatewayFlowRule> rules = new HashSet<>();
rules.add(new GatewayFlowRule("user")
.setResourceMode(SentinelGatewayConstants.RESOURCE_MODE_CUSTOM_API_NAME)
.setCount(2) // 限流阈值
.setIntervalSec(1) // 统计时间窗口,单位是秒,默认是 1 秒
);
rules.add(new GatewayFlowRule("product")
.setResourceMode(SentinelGatewayConstants.RESOURCE_MODE_CUSTOM_API_NAME)
.setCount(1000) // 限流阈值
.setIntervalSec(1) // 统计时间窗口,单位是秒,默认是 1 秒
);
// 手动加载网关规则
GatewayRuleManager.loadRules(rules);
}
}
5. 自定义异常
/**
* 初始化自定义异常
* @date: 2021/4/20 17:21
*/
private void initBlockExceptionHandler() {
BlockRequestHandler blockRequestHandler = (serverWebExchange, throwable) -> {
String value = serverWebExchange.getRequest().getPath().pathWithinApplication().value();
Result<String> failed = Result.failed(ResultCode.TOO_MANY_REQUESTS, value);
log.error("initBlockExceptionHandler.resp failed={}",failed);
return ServerResponse
.status(HttpStatus.TOO_MANY_REQUESTS)
.contentType(MediaType.APPLICATION_JSON_UTF8)
.body(BodyInserters.fromObject(failed));
};
GatewayCallbackManager.setBlockHandler(blockRequestHandler);
}
四、启动服务
多次请求 curl http://localhost:9000/api
配置限流API分组规则如下
1. 实时监控数据
2. 簇点链路
在右侧可以设置流控、降级、热点、授权操作
流控设置
我们设置单机阈值为 1 测试,每秒之内只允许 1 个访问请求
测试结果
再次访问 http://localhost:9000/api 接口
{"code":429,"message":"Too Many Requests","data":"/api"}
对应的参数属性
- resource:资源名,即限流规则的作用对象
- count: 限流阈值
- grade: 限流阈值类型(QPS 或并发线程数)
- limitApp: 流控针对的调用来源,若为 default 则不区分调用来源
- strategy: 调用关系限流策略
- controlBehavior: 流量控制效果(直接拒绝、Warm Up、匀速排队)
五、总结
以上就是对Spring Cloud gateway 与 Sentinel 的整合方案,下一篇会介绍如何持久化我们配置信息。
源码地址
mall-gateway 这个项目
https://gitee.com/gaibianzlp/zlp-mall-demo.git
参考链接
1. Spring Cloud gateway 五 Sentinel整合
https://blog.csdn.net/autfish/article/details/90405679
相关文章
- 网关概念+Nginx正反向代理+负载均衡+Spring Cloud Gateway
- Spring整合Mybatis极简完整示例(结构清晰仅需一个xml系统配置文件!)
- Spring Boot动态修改日志级别
- Spring Cloud Feign 请求添加headers(转载)
- 使用 Spring Cloud Stream 构建消息驱动微服务
- Spring Cloud Feign 声明式服务调用
- 服务治理: Spring Cloud Eureka
- 漏洞复现----42、Spring Cloud Gateway Actuator API SpEL表达式注入命令执行(CVE-2022-22947)
- Spring源码分析(八)Spring 所有BeanFactoryPostProcessor扩展接口
- Spring Cloud 和 Dubbo 哪个会被淘汰?
- Spring Boot 2.x基础教程:找回启动日志中的请求路径列表
- Spring Cloud Config采用数据库存储配置内容
- Spring AOP 不同配置方式产生的冲突问题
- 江帅帅:Spring Boot 底层级探索系列 02 - 自动配置的底层逻辑
- Spring Cloud 使用Feign调用微服务出现:QueryMap key must be a String:Long异常
- Spring集成Redis集群(含spring集成redis代码)
- 从零搭建Spring Boot脚手架(3):集成mybatis
- 远程代码执行漏洞Spring Cloud Gateway RCE(CVE-2022-22947)
- 【Spring Cloud】如何安装与配置Nacos注册中心?
- 【Spring Cloud】Ribbon负载均衡原理与实战(源码级讲解)
- Spring之IOC实现原理
- spring-cloud-zuul
- Spring Remoting: Burlap--转
- 使用 Spring 2.5 基于注解驱动的 Spring MVC--转
- Spring Tools Suite 4安装