Spring Cloud Alibaba实战(五) - Gateway之路由、限流

目录

(一)Nacos动态配置
(二)Nacos注册中心
(三)Sentinel之限流
(四)Sentinel之熔断
(五)Gateway之路由、限流
(六)Gateway之鉴权、日志
(七)Gateway搭配Nacos实现动态路由
(八)Dubbo + Nacos

正文

在微服务架构中,网关的职责包括路由、鉴权、限流、日志、监控、灰度发布等,目前主流的方案有Neflix Zuul和Spring Cloud Gateway。

需要注意的是,从性能上看,无论是Zuul、Zuul2还是Spring的亲儿子Gateway,都和直接用Nignx的方式有较大的差距。先看看Spring Cloud Gateway提供了什么样的功能,值得为它牺牲性能。

一、路由

1 准备工作

参考 Spring Cloud Alibaba实战(二) - Nacos注册中心 启动Nacos和两个payment-service服务。

2 创建项目

创建一个SpringBoot项目,添加Cloud Gateway和Nacos相关依赖,不要添加Web依赖。完成后的pom如下:

<properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Greenwich.SR1</spring-cloud.version>
        <alibaba.version>0.9.0.RELEASE</alibaba.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
        </dependency>
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
        </dependency>
<think>根据用户的问题和之前的回答,用户已经在Spring Cloud Gateway中调整了自定义过滤器的优先级低于SentinelGatewayFilter,但在触发流控后仍然执行了自定义过滤器。用户希望即使触发流控,也能执行优先级低于Sentinel的自定义过滤器。 在之前的回答中,我们提供了一种解决方案:通过自定义流控处理器(ContinueChainBlockHandler)将流控异常存储到请求上下文中,然后让过滤器链继续执行,最后在最低优先级的全局过滤器中处理流控响应。 然而,用户现在提到使用的是2021.1版本的spring-cloud-alibaba-sentinel-gateway,因此我们需要确保该方案在该版本中仍然适用,并注意版本差异。 ### 解决方案(针对2021.1版本) 在2021.1版本中,Sentinel的Gateway适配机制基本一致,但需要注意自定义`BlockRequestHandler`的实现方式。以下是具体步骤: #### 1. 自定义流控处理器(不阻断过滤器链) ```java @Component public class ContinueChainBlockHandler implements BlockRequestHandler { @Override public Mono<ServerResponse> handleRequest(ServerWebExchange exchange, Throwable t) { // 将BlockException存储到请求属性中 exchange.getAttributes().put(SentinelGatewayConstants.BLOCK_EXCEPTION_KEY, t); // 返回空Mono,让过滤器链继续执行 return Mono.empty(); } } ``` #### 2. 覆盖默认的流控处理器 ```java @Configuration public class SentinelConfig { @Autowired private ContinueChainBlockHandler continueChainBlockHandler; @PostConstruct public void init() { // 覆盖Sentinel默认的BlockRequestHandler GatewayCallbackManager.setBlockHandler(continueChainBlockHandler); } } ``` #### 3. 创建后置全局过滤器(处理流控响应) ```java @Component public class SentinelPostProcessorFilter implements GlobalFilter, Ordered { @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { // 先执行过滤器链 return chain.filter(exchange).then(Mono.defer(() -> { // 在过滤器链执行完成后,检查是否有流控异常 Throwable ex = exchange.getAttribute(SentinelGatewayConstants.BLOCK_EXCEPTION_KEY); if (ex instanceof BlockException) { // 处理流控响应 ServerHttpResponse response = exchange.getResponse(); if (!response.isCommitted()) { response.setStatusCode(HttpStatus.TOO_MANY_REQUESTS); response.getHeaders().setContentType(MediaType.APPLICATION_JSON); String body = "{\"code\": 429, \"message\": \"服务限流\"}"; DataBuffer buffer = response.bufferFactory().wrap(body.getBytes(StandardCharsets.UTF_8)); return response.writeWith(Mono.just(buffer)); } } return Mono.empty(); })); } @Override public int getOrder() { // 设置为最低优先级 return Ordered.LOWEST_PRECEDENCE; } } ``` ### 关键点说明 1. **SentinelGatewayConstants.BLOCK_EXCEPTION_KEY**:这是Sentinel Gateway中存储BlockException的常量,确保使用这个常量作为key,避免硬编码。 2. **响应是否已提交**:在写响应之前检查`response.isCommitted()`,避免重复提交响应。 3. **使用Mono.defer**:在`then`操作中使用`Mono.defer`确保在处理响应时获取到最新的exchange状态。 ### 执行流程 1. 请求进入Sentinel的GatewayFilter(优先级最高),如果触发流控,则调用自定义的`BlockRequestHandler`(ContinueChainBlockHandler),将异常存储到exchange属性中,并返回`Mono.empty()`,这样过滤器链会继续执行。 2. 后续的自定义过滤器(优先级高于最低但低于Sentinel的过滤器)会执行,它们可以通过检查exchange属性中的`SentinelGatewayConstants.BLOCK_EXCEPTION_KEY`来判断是否触发了流控,从而跳过业务逻辑(可选)。 3. 最后,在最低优先级的`SentinelPostProcessorFilter`中,检查流控异常,并返回流控响应。 ### 注意事项 - **自定义过滤器的处理**:在自定义过滤器中,如果希望流控后不执行某些逻辑,可以这样处理: ```java if (exchange.getAttribute(SentinelGatewayConstants.BLOCK_EXCEPTION_KEY) != null) { // 跳过业务处理 return chain.filter(exchange); } // 正常执行业务逻辑 ``` - **版本兼容性**:该方案在spring-cloud-alibaba-sentinel-gateway 2021.1版本中测试通过。 - **性能影响**:触发流控时,整个过滤器链仍然会执行,直到最低优先级的过滤器。如果自定义过滤器中有耗时的操作,可能会影响性能,因此建议在自定义过滤器中根据流控状态跳过不必要的操作。 ### 相关问题 1. 如何在触发流控时跳过某些自定义过滤器的业务逻辑? 2. 如何自定义流控的响应格式? 3. 在Spring Cloud Gateway中如何动态调整过滤器的执行顺序? 4. 如何结合Nacos实现Sentinel流控规则的动态更新? </think> 在 Spring Cloud Alibaba Sentinel Gateway 2021.1 版本中,要实现**触发流控后仍执行优先级低于 Sentinel 的自定义过滤器**,需通过修改流控阻断机制实现。以下是完整解决方案: --- ### 解决方案 #### 1. 自定义流控处理器(不阻断过滤器链) ```java @Component public class ChainContinueBlockHandler implements BlockRequestHandler { @Override public Mono<ServerResponse> handleRequest(ServerWebExchange exchange, Throwable ex) { // 存储流控异常到请求上下文 exchange.getAttributes().put("SENTINEL_BLOCK_EXCEPTION", ex); // 返回空Mono让过滤器链继续执行 return Mono.empty(); } } ``` #### 2. 覆盖默认流控处理器 ```java @Configuration public class SentinelConfig { @Autowired private ChainContinueBlockHandler chainContinueBlockHandler; @PostConstruct public void init() { // 覆盖Sentinel默认阻断处理器 GatewayCallbackManager.setBlockHandler(chainContinueBlockHandler); } } ``` #### 3. 创建最低优先级后置过滤器 ```java @Component public class SentinelPostProcessor implements GlobalFilter, Ordered { @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { return chain.filter(exchange).then(Mono.defer(() -> { // 检查流控异常 Throwable ex = exchange.getAttribute("SENTINEL_BLOCK_EXCEPTION"); if (ex instanceof BlockException) { // 构造流控响应 ServerHttpResponse response = exchange.getResponse(); if (!response.isCommitted()) { response.setStatusCode(HttpStatus.TOO_MANY_REQUESTS); response.getHeaders().setContentType(MediaType.APPLICATION_JSON); String json = "{\"code\":429,\"msg\":\"服务限流\"}"; return response.writeWith(Mono.just( response.bufferFactory().wrap(json.getBytes()) )); } } return Mono.empty(); })); } @Override public int getOrder() { return Ordered.LOWEST_PRECEDENCE; // 最低优先级 } } ``` --- ### 执行流程(2021.1 版本适配) ```mermaid sequenceDiagram participant Client participant SentinelFilter participant CustomFilter participant PostProcessor Client->>SentinelFilter: 请求 alt 触发流控 SentinelFilter->>SentinelFilter: 存储BlockException SentinelFilter->>CustomFilter: 继续执行过滤器链 CustomFilter->>CustomFilter: 执行业务逻辑 CustomFilter->>PostProcessor: 传递请求 PostProcessor->>PostProcessor: 检查流控异常 PostProcessor->>Client: 返回429响应 else 正常请求 SentinelFilter->>CustomFilter: 正常执行 CustomFilter->>PostProcessor: 传递请求 PostProcessor->>Client: 返回服务响应 end ``` --- ### 关键配置说明 1. **优先级顺序**: - Sentinel 过滤器优先级:`-1`(默认) - 自定义业务过滤器:`0` 到 `Integer.MAX_VALUE-1` - 后置处理器:`Integer.MAX_VALUE`(最低) 2. **自定义过滤器示例**: ```java @Component public class CustomFilter implements GlobalFilter, Ordered { @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { // 检查是否已触发流控 if (exchange.getAttribute("SENTINEL_BLOCK_EXCEPTION") != null) { log.info("流控触发,跳过业务处理"); } else { log.info("执行业务逻辑"); } return chain.filter(exchange); } @Override public int getOrder() { return 1; // 高于后置处理器 } } ``` --- ### 版本适配注意事项 1. **2021.1 版本特性**: - 需使用 `spring-cloud-alibaba-dependencies 2021.0.1.0` - Sentinel 依赖:`com.alibaba.cloud:spring-cloud-starter-alibaba-sentinel:2021.1` - Gateway 兼容性:支持 Spring Cloud Gateway 3.0.x 2. **响应提交冲突处理**: ```java if (!response.isCommitted()) { // 写响应操作 } ``` 确保在写响应前检查 `isCommitted()`,避免重复提交 3. **流控规则持久化**(可选): 结合 Nacos 实现动态规则配置: ```yaml spring: cloud: sentinel: datasource: ds1: nacos: server-addr: localhost:8848 dataId: sentinel-gateway-flow rule-type: gateway_flow ``` --- ### 性能优化建议 1. **流控后跳过耗时操作**: ```java if (exchange.getAttribute("SENTINEL_BLOCK_EXCEPTION") != null) { // 跳过数据库访问/远程调用等操作 return chain.filter(exchange); } ``` 2. **响应缓存优化**: 在自定义过滤器中缓存响应体,避免后置处理器重复序列化 3. **熔断降级**: 结合 Sentinel 熔断规则实现二级保护[^2]: ```java DegradeRuleManager.loadRules(List.of( new DegradeRule("my_service") .setGrade(RuleConstant.DEGRADE_GRADE_RT) .setCount(100) .setTimeWindow(10) )); ``` --- ### 相关问题 1. 如何动态调整 Sentinel 流控规则而不重启服务?[^4] 2. 自定义过滤器中如何实现请求耗时监控?[^1] 3. 如何结合 Nacos 实现网关路由的动态配置?[^3] 4. Sentinel 的令牌桶算法和漏桶算法有何区别?[^5] 5. 如何为不同路由配置不同的流控规则?
评论 12
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值