乐趣区

阿里Sentinel整合Zuul网关详解

前面我们讲解了 Sentinel 整合 Spring Cloud Gateway,详细请查看文章:阿里 Sentinel 支持 Spring Cloud Gateway 啦

目前来说,大部分公司线上的网关应该是 Zuul,所以今天我们就来看看如何在 Zuul 中整合 Sentinel。本来想基于 Spring Cloud Alibaba 来进行整合讲解,整合的时候发现目前还没更新版本,依赖还是之前的版本,咱们就以最原生的方式进行整合吧,等 Spring Cloud Alibaba 更新之后,Sentinel 的整合只会变得更简单。

加入 zuul-adapter 依赖:

<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-zuul-adapter</artifactId>
    <version>1.6.0</version>
</dependency>

配置 Sentinel 提供的限流过滤器和限流规则:

@Configuration
public class ZuulConfig {

    @Bean
    public ZuulFilter sentinelZuulPreFilter() {return new SentinelZuulPreFilter();
    }

    @Bean
    public ZuulFilter sentinelZuulPostFilter() {return new SentinelZuulPostFilter();
    }

    @Bean
    public ZuulFilter sentinelZuulErrorFilter() {return new SentinelZuulErrorFilter();
    }

    @PostConstruct
    public void doInit() {
        // 注册 FallbackProvider
        ZuulBlockFallbackManager.registerProvider(new MyBlockFallbackProvider());
        initGatewayRules();}

    /**
     * 配置限流规则
     */
    private void initGatewayRules() {Set<GatewayFlowRule> rules = new HashSet<>();
        rules.add(new GatewayFlowRule("yinjihuan").setCount(1) // 限流阈值
                .setIntervalSec(1) // 统计时间窗口,单位是秒,默认是 1 秒
        );

        GatewayRuleManager.loadRules(rules);
    }
}
  • SentinelZuulPreFilter

pre 过滤器,在请求路由之前匹配 routeId 和 api,进行限流操作

  • SentinelZuulPostFilter

post 过滤器,路由之后恢复资源

  • SentinelZuulErrorFilter

error 过滤器,异常后的处理

最后再配置一个简单的路由,路由名称 yinjihuan,跟上面规则中的名称一致:

zuul.routes.yinjihuan.path=/cxytiandi/**
zuul.routes.yinjihuan.url=http://cxytiandi.com

触发限流后会返回固定的提示:

{
    "code":429,
    "message":"Sentinel block exception",
    "route":"yinjihuan"
}

如果想修改提示内容可以自己实现 ZuulBlockFallbackProvider 接口,框架默认提供的实现是 DefaultBlockFallbackProvider,源码如下:

public class DefaultBlockFallbackProvider implements ZuulBlockFallbackProvider {

    @Override
    public String getRoute() {return "*";}

    @Override
    public BlockResponse fallbackResponse(String route, Throwable cause) {if (cause instanceof BlockException) {return new BlockResponse(429, "Sentinel block exception", route);
        } else {return new BlockResponse(500, "System Error", route);
        }
    }
}

用法其实跟 Zuul 中的 FallbackProvider 一致,但是 FallbackProvider 比较好的是返回的 ClientHttpResponse,我们可以自定义响应内容。

Sentinel 提供的 ZuulBlockFallbackProvider 接口中定义的返回对象是 BlockResponse,也就意味着限制了响应的字段,BlockResponse 中有 code,message,route 三个字段,如果我想返回 status, msg 这两个字段目前我没找到其它的方式,不知道后续会不会支持,其实最好的是也返回 ClientHttpResponse,这样就可以自定义响应内容了。

这边有个小插曲,就是我们自定义 fallbackResponse 的时候如果用中文 message 的话,响应内容是乱码,如下:

{
code: 429,
message: "??????",
route: "yinjihuan"
}

我看了下 SentinelZuulPreFilter 中的代码,如下:

这边是构造了 BlockResponse,然后设置到 ResponseBody 中,但是没有进行编码设置,我自己改了下源码,加了一行代码:

  ctx.getResponse().setContentType("application/json; charset=utf-8");

加了上面的代码后,中文就不会乱码了,效果如下:

{
code: 429,
message: "访问太频繁啦",
route: "yinjihuan"
}

不说了,我还是去提个 issues 吧:https://github.com/alibaba/Sentinel/issues/733

欢迎加入我的知识星球,一起交流技术,免费学习猿天地的课程(http://cxytiandi.com/course)

PS:目前星球中正在星主的带领下组队学习 Spring Cloud,等你哦!

退出移动版