Spring Cloud限流详解

Spring Cloud Spring Cloud 2017/12/01

在高并发的应用中,限流往往是一个绕不开的话题。本文详细探讨在Spring Cloud中如何实现限流。

Zuul上实现限流是个不错的选择,只需要编写一个过滤器就可以了,关键在于如何实现限流的算法。常见的限流算法有漏桶算法以及令牌桶算法。这个可参考https://www.cnblogs.com/LBSer/p/4083131.html,写得通俗易懂,你值得拥有,我就不拽文了。

Google Guava为我们提供了限流工具类RateLimiter,于是乎,我们可以撸代码了。

代码示例

   @Componentpublic class RateLimitZuulFilter extends ZuulFilter {private final RateLimiter rateLimiter = RateLimiter.create(1000.0);@Overridepublic String filterType() {return FilterConstants.PRE\_TYPE;}@Overridepublic int filterOrder() {return Ordered.HIGHEST\_PRECEDENCE;}@Overridepublic boolean shouldFilter() {// 这里可以考虑弄个限流开启的开关,开启限流返回true,关闭限流返回false,你懂的。return true;}@Overridepublic Object run() {try {RequestContext currentContext = RequestContext.getCurrentContext();HttpServletResponse response = currentContext.getResponse();if (!rateLimiter.tryAcquire()) {HttpStatus httpStatus = HttpStatus.TOO\_MANY\_REQUESTS;response.setContentType(MediaType.TEXT\_PLAIN\_VALUE);response.setStatus(httpStatus.value());response.getWriter().append(httpStatus.getReasonPhrase());currentContext.setSendZuulResponse(false);throw new ZuulException(httpStatus.getReasonPhrase(),httpStatus.value(),httpStatus.getReasonPhrase());}} catch (Exception e) {ReflectionUtils.rethrowRuntimeException(e);}return null;}}

如上,我们编写了一个pre类型的过滤器。对Zuul过滤器有疑问的可参考我的博客:

  • Spring Cloud内置的Zuul过滤器详解:http://www.itmuch.com/spring-cloud/zuul/zuul-filter-in-spring-cloud

  • Spring Cloud Zuul过滤器详解:http://www.itmuch.com/spring-cloud/zuul/spring-cloud-zuul-filter

在过滤器中,我们使用Guava RateLimiter实现限流,如果已经达到最大流量,就抛异常。

分布式场景下的限流

以上单节点Zuul下的限流,但在生产中,我们往往会有多个Zuul实例。对于这种场景如何限流呢?我们可以借助Redis实现限流。

使用redis实现,存储两个key,一个用于计时,一个用于计数。请求每调用一次,计数器增加1,若在计时器时间内计数器未超过阈值,则可以处理任务

   if(!cacheDao.hasKey(TIME\_KEY)) {cacheDao.putToValue(TIME\_KEY, 0, 1, TimeUnit.SECONDS);}       if(cacheDao.hasKey(TIME\_KEY) && cacheDao.incrBy(COUNTER\_KEY, 1) > 400) {// 抛个异常什么的}

实现微服务级别的限流

一些场景下,我们可能还需要实现微服务粒度的限流。此时可以有两种方案:

方式一:在微服务本身实现限流。

和在Zuul上实现限流类似,只需编写一个过滤器或者拦截器即可,比较简单,不作赘述。个人不太喜欢这种方式,因为每个微服务都得编码,感觉成本很高啊。

加班那么多,作为程序猿的我们,应该学会偷懒,这样才可能有时间孝顺父母、抱老婆、逗儿子、遛狗养鸟、聊天打屁、追求人生信仰。好了不扯淡了,看方法二吧。

方法二:在Zuul上实现微服务粒度的限流。

在讲解之前,我们不妨模拟两个路由规则,两种路由规则分别代表Zuul的两种路由方式。

   zuul:routes:microservice-provider-user: /user/\*\*user2:url: http://localhost:8000/path: /user2/\*\*

如配置所示,在这里,我们定义了两个路由规则,microservice-provider-user以及user2,其中microservice-provider-user这个路由规则使用到Ribbon + Hystrix,走的是RibbonRoutingFilter;而user2这个路由用不上Ribbon也用不上Hystrix,走的是SipleRoutingFilter。如果你搞不清楚这点,请参阅我的博客:

搞清楚这点之后,我们就可以撸代码了:

   @Componentpublic class RateLimitZuulFilter extends ZuulFilter {private Map<String, RateLimiter> map = Maps.newConcurrentMap();@Overridepublic String filterType() {return FilterConstants.PRE\_TYPE;}@Overridepublic int filterOrder() {// 这边的order一定要大于org.springframework.cloud.netflix.zuul.filters.pre.PreDecorationFilter的order// 也就是要大于5// 否则,RequestContext.getCurrentContext()里拿不到serviceId等数据。return Ordered.LOWEST\_PRECEDENCE;}@Overridepublic boolean shouldFilter() {// 这里可以考虑弄个限流开启的开关,开启限流返回true,关闭限流返回false,你懂的。return true;}@Overridepublic Object run() {try {RequestContext context = RequestContext.getCurrentContext();HttpServletResponse response = context.getResponse();String key = null;// 对于service格式的路由,走RibbonRoutingFilterString serviceId = (String) context.get(SERVICE\_ID\_KEY);if (serviceId != null) {key = serviceId;map.putIfAbsent(serviceId, RateLimiter.create(1000.0));}// 如果压根不走RibbonRoutingFilter,则认为是URL格式的路由else {// 对于URL格式的路由,走SimpleHostRoutingFilterURL routeHost = context.getRouteHost();if (routeHost != null) {String url = routeHost.toString();key = url;map.putIfAbsent(url, RateLimiter.create(2000.0));}}RateLimiter rateLimiter = map.get(key);if (!rateLimiter.tryAcquire()) {HttpStatus httpStatus = HttpStatus.TOO\_MANY\_REQUESTS;response.setContentType(MediaType.TEXT\_PLAIN\_VALUE);response.setStatus(httpStatus.value());response.getWriter().append(httpStatus.getReasonPhrase());context.setSendZuulResponse(false);throw new ZuulException(httpStatus.getReasonPhrase(),httpStatus.value(),httpStatus.getReasonPhrase());}} catch (Exception e) {ReflectionUtils.rethrowRuntimeException(e);}return null;}}

简单讲解一下这段代码:

对于microservice-provider-user这个路由,我们可以用context.get(SERVICE_ID_KEY);获取到serviceId,获取出来就是microservice-provider-user

而对于user2这个路由,我们使用context.get(SERVICE_ID_KEY);获得是null,但是呢,可以用context.getRouteHost()获得路由到的地址,获取出来就是http://localhost:8000/。接下来的事情,你们懂的。

改进与提升

实际项目中,除以上实现的限流方式,还可能会:

一、在上文的基础上,增加配置项,控制每个路由的限流指标,并实现动态刷新,从而实现更加灵活的管理

二、基于CPU、内存、数据库等压力限流(感谢平安常浩智)提出。。

下面,笔者借助Spring Boot Actuator提供的Metrics能力进行实现基于内存压力的限流——当可用内存低于某个阈值就开启限流,否则不开启限流。

   @Componentpublic class RateLimitZuulFilter extends ZuulFilter {@Autowiredprivate SystemPublicMetrics systemPublicMetrics;@Overridepublic boolean shouldFilter() {// 这里可以考虑弄个限流开启的开关,开启限流返回true,关闭限流返回false,你懂的。Collection<Metric<\>> metrics = systemPublicMetrics.metrics();Optional<Metric<\>> freeMemoryMetric = metrics.stream().filter(t \-\> "mem.free".equals(t.getName())).findFirst();// 如果不存在这个指标,稳妥起见,返回true,开启限流if (!freeMemoryMetric.isPresent()) {return true;}long freeMemory = freeMemoryMetric.get().getValue().longValue();// 如果可用内存小于1000000KB,开启流控return freeMemory < 1000000L;}// 省略其他方法}

三、实现不同维度的限流,例如:

参考文档

Spring Cloud限流详解相关推荐

  1. Spring Cloud限流详解(附源码)

    在高并发的应用中,限流往往是一个绕不开的话题.本文详细探讨在Spring Cloud中如何实现限流. 在 Zuul 上实现限流是个不错的选择,只需要编写一个过滤器就可以了,关键在于如何实现限流的算法. ...

  2. Spring Cloud限流详解(内含源码)

    为什么80%的码农都做不了架构师?>>>    原文:http://www.itmuch.com/spring-cloud-sum/spring-cloud-ratelimit/ 在 ...

  3. spring-clou限流详解

    在高并发的应用中,限流往往是一个绕不开的话题.本文详细探讨在Spring Cloud中如何实现限流. 在Zuul 上实现限流是个不错的选择,只需要编写一个过滤器就可以了,关键在于如何实现限流的算法.常 ...

  4. Spring Cloud Feign使用详解

     通过前面两章对Spring Cloud Ribbon和Spring Cloud Hystrix的介绍,我们已经掌握了开发微服务应用时,两个重要武器,学会了如何在微服务架构中实现客户端负载均衡的服务调 ...

  5. 微服务网关spring cloud gateway入门详解

    1.API网关 API 网关是一个处于应用程序或服务( REST API 接口服务)之前的系统,用来管理授权.访问控制和流量限制等,这样 REST API 接口服务就被 API 网关保护起来,对所有的 ...

  6. Spring Cloud Gateway 过滤器详解

    一.概述 Spring Cloud Gateway根据作用范围划分为:GatewayFilter和GlobalFilter 1.filter的作用和生命周期 由filter工作流程点,可以知道filt ...

  7. spring cloud各组件详解

    一,Eureka 服务注册中心,特性有失效剔除.服务保护.健康检查. 二.Ribbon 客户端负载均衡,特性有区域亲和.重试机制. 三.Hystrix 客户端容错保护,特性有服务降级.服务熔断.请求缓 ...

  8. SpringCloud入门之应用程序上下文服务(Spring Cloud Context)详解

    构建分布式系统非常复杂且容易出错.Spring Cloud为最常见的分布式系统模式提供了简单易用的编程模型,帮助开发人员构建弹性,可靠和协调的应用程序.Spring Cloud构建于Spring Bo ...

  9. Spring Cloud Gateway配置详解-过滤器

    Spring Cloud Gateway-过滤器 本节将为大家详细介绍Spring Could Gateway 内置过滤器相关内容. Spring Cloud Gateway 过滤器为大家提供了修改特 ...

  10. 基于Redis的分布式限流详解

    前言 Redis除了能用作缓存外,还有很多其他用途,比如分布式锁,分布式限流,分布式唯一主键等,本文将和大家分享下基于Redis分布式限流的各种实现方案. 一.为什么需要限流 用最简单的话来说:外部请 ...

最新文章

  1. android使用C/C++调用SO库
  2. linux的rt补丁安装,微软发布补丁封杀允许Surface RT安装Linux的“漏洞”
  3. qt qtreewidget 设置 值_QTreeWidget
  4. ORACLE11G RAC增加节点操作
  5. VHDL 整数 小数 分数 分频
  6. Oracle游标使用大全
  7. 获得邮件列表失败_邮件经常失败回弹很糟心?一定要知道这几个小知识
  8. python 连接 mysql
  9. BMW Trojan 样本分析
  10. 博世BMI160驱动程序 C语言编程,如何使用PSoC 6制作完整的测试系统来与BMI160进行通话...
  11. c4d渲染测试软件,提升C4D渲染速度,你需要的高端显卡选购指南
  12. java speex回声消除_android 利用speex 音频降噪,回声消除demo
  13. 张继之为担任BCF理事
  14. Linux之查看物理主机的CPU温度
  15. AssertionError: CUDA unavailable, invalid device 0 requested
  16. 开源实时数据库_实时应用程序的开源数据库
  17. 解决笔记本电脑开机速度缓慢的几种措施,亲测有效
  18. 【PBR系列三】BRDF方程及渲染方程
  19. Android 自动抓取网站图标实现分享样式的定制
  20. 峰值预测性能指标PPTS(Peak percentage of threshold statistic)

热门文章

  1. java 将对象置空_Java 中将对象引用置 null 的作用?
  2. 在计算机里看不到硬盘的信息,检测不到硬盘,详细教您系统里找不到硬盘该怎么办...
  3. mac打开注册机显示“您没有权限来打开应用程序
  4. 我们不应该歧视任何的编程语言,因为他们都是萌娘
  5. 阿里云服务器价格表 ECS最新价格优惠汇总
  6. wav 文件头 删除_Yate for mac(音频文件标记和管理工具)
  7. ios ping服务器
  8. java替换图片文字_Java 替换PPT文档中的文本和图片
  9. 陈龙杰计算机专业,第四届学生职业技能大赛获奖名单
  10. Allure趋势图本地显示