前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >技术硬实力,微信公众平台都用到了分布式限流技术,我们为什么不用呢?

技术硬实力,微信公众平台都用到了分布式限流技术,我们为什么不用呢?

作者头像
35岁程序员那些事
发布2022-09-23 18:06:10
5940
发布2022-09-23 18:06:10
举报

笔者一直在使用微信的微信公众号平台去管理公众号中的技术文章,但是今天多刷了几下,居然就被限流了,没错就连这样的纯碎面向B端(就像我们这些公众号主)的后台管理系统都用到了限流技术,那么我们面向C端的业务产品凭什么还不用呢?

谈谈分布式限流的背景

首先,当我们的业务接口需要暴露在公网之中,直接服务于C端用户,无论是手机端还是PC端,只要并发量上去之后,都会存在各种网络超时,有超时就会存在重试,也就是说如果100TPS的请求通过弱网环境打到了一个系统不稳定的服务上,那么需要同时处理的请求的次数是远远大于100次的(有可能是好几倍),这个时候不稳定的服务,就极可能会直接被冲垮,从而导致完全的不可用;

如何去实现分布式限流

一般情况下,我们必须要先完成本地限流,要实现本地限流我建议大家可以按照如下方式去实现。

(1)布隆过滤器

有人也许会认为“布隆过滤器”也算是本地限流?这个是有一点牵强吧,顾名思义,布隆过滤器起到的是过滤的作用,在缓存穿透的场景下,过滤掉肯定不在系统中 key 的相关请求。所以,布隆过滤器,核心就是要维护一个数据结构,我们通过它来快速判断某个 key 是否存在于某个集合中。利用“布隆过滤器”可以将步合法的流量隔离到业务服务的最前端,当然过滤的规则是可以动态调整的。关于布隆过滤器的原理我这里就介绍了,我只是告诉大家用它也是可以实现限流的。

我们最常用的布隆过滤器主要有如下两种方式:

  • 基于Redission的SDK,这个主要是利用Redis中的数据结构来实现的;
  • 基于Guava的SDK,谷歌的Guava工具包也实现了本地的布隆过滤器的数据结构,一般情况下,如果不需要保障分布式环境下的强一致性,建议大家使用谷歌的Guava工具包去实现布隆过滤器,效率是非常高的。

(2)Guava

关于具体算法细节,大家可以自行查阅一下。可以参考官方地址:https://github.com/google/guava

Guava中的限流使用的是令牌桶算法,RateLimiter提供了两种限流实现:

  • 平滑突发限流(SmoothBursty),每秒以固定的速率输出令牌,以达到平滑输出的效果;
  • 平滑预热限流(SmoothWarmingUp),平滑预热限流带有预热期的平滑限流,它启动后会有一段预热期,逐步将令牌产生的频率提升到配置的速率,这种方式适用于系统启动后需要一段时间来进行预热的场景。比如,我设置的是每秒5个令牌,预热期为5秒,那么它就不会是0.2左右产生一个令牌。在前5秒钟它不是一个均匀的速率,5秒后恢复均匀的速率。

(3)Java容器和队列

Java容器和队列能够起到限流这个就很好理解了,无论是哪一种容器和队列,都能够起到本地缓存的作用,并且这些缓存在设定了容量之后,都能够自动的去丢弃需要存储的请求,当然我们也可以自定义丢弃策略,这一点可以和线程池的设计思想保持一致。

(4)Hystrix

Hystrix这个我就不多说了,它是Spring Cloud最出彩的微服务架构技术解决方案之一,很多公司都是直接采用它来做本地限流,尤其是业务网关中。

可以参考官网地址:https://github.com/Netflix/Hystrix

Hystrix提供了信号量和线程池两种隔离方式,默认是线程池,这种情况下,方法实际执行都是在Hystrix的线程池中,这就涉及到了线程切换,需要从当前线程切换到Hystrix的线程池中。线程池带来的好处是比较明显的,同一个资源采用的是同一个线程池不会影响其他资源的线程池,当某个资源调用出现问题的时候,只会影响该资源不会影响其他资源。

(5)线程池

线程池本来就是利用多线程加队列技术来实现线程复用的,因为存在丢弃策略,所以也是具备本地限流的策略的。关于Java线程池的原理,这里就不描述了,网上的文章太多了。

(6)Resilience4j

Resilience4j是一个轻量级、易于使用的容错库,其灵感来自Netflix Hystrix,但专为Java 8和函数式编程设计。Resilience4j提供了两种舱壁模式的实现,可用于限制并发执行的次数:

  • SemaphoreBulkhead(信号量舱壁,默认),基于Java并发库中的Semaphore实现。SemaphoreBulkhead在各种线程和I / O模型上都能很好地工作,主要原因是它基于信号量,与Hystrix不同,它不提供“影子”线程池选项,它主要取决于客户端,以确保正确的线程池大小将与舱壁配置保持一致;
  • FixedThreadPoolBulkhead(固定线程池舱壁),它使用一个有界队列和一个固定线程池。

SemaphoreBulkhead也叫信号量舱壁,当信号量存在剩余时进入系统的请求会直接获取信号量并开始业务处理。当信号量全被占用时,接下来的请求将会进入阻塞状态,SemaphoreBulkhead提供了一个阻塞计时器,如果阻塞状态的请求在阻塞计时内无法获取到信号量则系统会拒绝这些请求。若请求在阻塞计时内获取到了信号量,那将直接获取信号量并执行相应的业务处理。

FixedThreadPoolBulkhead也叫固定线程池舱壁,FixedThreadPoolBulkhead的功能与SemaphoreBulkhead一样也是用于限制并发执行的次数的,但是二者的实现原理存在差别而且表现效果也存在细微的差别。FixedThreadPoolBulkhead使用一个固定线程池和一个等待队列来实现舱壁。当线程池中存在空闲时,则此时进入系统的请求将直接进入线程池开启新线程或使用空闲线程来处理请求。当线程池无空闲时接下来的请求将进入等待队列,若等待队列仍然无剩余空间时接下来的请求将直接被拒绝。在队列中的请求等待线程池出现空闲时,将进入线程池进行业务处理。

总之,可以看出FixedThreadPoolBulkhead和SemaphoreBulkhead一个明显的差别就是FixedThreadPoolBulkhead没有阻塞的概念,而SemaphoreBulkhead没有一个队列容量的限制,它是非阻塞的。

大家可以参考开源地址:https://github.com/resilience4j/resilience4j

(7)Bucket4j

Bucket4j 是基于令牌桶算法的 Java 限流库,它主要用在 3 种场景:

  • 限制比较重工作的速率;
  • 限制对 API 的访问速率;
  • 将限流作为定时器,例如有些场景限制你对服务提供方的调用速度,因此使用限流器作为定时器,定时按照约定速率调用服务提供方。

当然我们可以使用Spring Boot去直接整合Bucket4j。

(8)其它开源解决方案

其它开源解决方案有很多,有些是一些基于业务的开源项目中使用的本地限流技术,这里就不列举了,大家如果能够充分的掌握上述本地限流技术,我想是能够应付绝大部分本地限流的业务场景了。

下面我们再来看一下分布式限流是如何实现的。

(1)Sentinel(分布式限流)

官方地址:https://github.com/alibaba/Sentinel

可以参考书籍Spring Cloud Alibaba微服务架构实战派上下册第六章,对着理论和例子跑通了就会了。

(2)Redis+Lua(分布式限流)

可以参考书籍Spring Cloud Alibaba微服务架构实战派上下册第九章,对着理论和例子跑通了就会了。

(3)Nginx

在Nginx侧,利用LUA脚本可以实现高性能的限流方案,这个主要是将限流点,完全隔离在业务服务之外,但是也会存在一个比较明显的弊端,那就死Nginx太重了,假如我们的运维不是很了解Nginx的原理,那就很容易出现问题。

(4)服务治理中的负载均衡

这个可以这样理解,当我们在微服务内部,利用负载均衡策略,比如按权重轮询、按响应时间和按照性能来进行负载均衡的时候,是可以实现将流量隔离在某些稳定性不是很好的服务实例对应的机器之外的。

(5)动态配置开关

这个其实就是利用配置中心去增加一些功能性的动态开关,比如当业务功能所在的服务实例出现问题的时候,我们就可以打开限流的开关,当然这些配置开关是要具备灰度的功能,这样就可以将流量隔离在当前实例或者当前实例所在的服务中的某些接口之外。

(6)黑白名单

黑白名单其实也是服务治理中的限流,就是在服务注册或者订阅阶段,读取黑白名单规则,这样就可以实现服务实例级别的限流。

(7)网关

网关主要是用来聚合业务API,并实现服务路由的。当然我们可以采用网关层的本地限流去实现业务全流程的限流,这样从某种程度上也实现了分布式限流,因为在一个网关实例中,就可以利用组合规则去实现不同服务的不同接口之间的级联限流。

再谈谈利用Sentinel实现分布式限流

在Spring Cloud Alibaba微服务架构实战派这本书中其实很详细的用实例串讲了Sentinel的使用方式:

(1)入门Sentinel,可以利用Spring Boot或者Spring Cloud Alibaba去从0-1的搭建微服务项目,并接入Sentinel控制台;

(2)用HTTP或者Netty实现通信渠道,让你放心的去使用Sentinel,因为Netty是高性能的;

(3)用过滤器和拦截器实现组件的适配,告诉你Sentinel的核心原理;

(4)用“流量防护”实现流量防护;

(5)用“熔断降级”实现流量防护;

(6)用“系统自适应保护”实现流量防护;

(7)用Nacos实现规则的动态配置和持久化。

如果大家想了解Sentinel的相关实践经验,可以购买Spring Cloud Alibaba微服务架构实战派上下册这本书。

公众号初衷

知识输出是笔者的初衷,借助知识输出,能够认识更多的牛人,能够和牛人沟通,也是自己技术提升的一个机会。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2022-07-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 架构随笔录 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 谈谈分布式限流的背景
  • 如何去实现分布式限流
  • 再谈谈利用Sentinel实现分布式限流
相关产品与服务
负载均衡
负载均衡(Cloud Load Balancer,CLB)提供安全快捷的流量分发服务,访问流量经由 CLB 可以自动分配到云中的多台后端服务器上,扩展系统的服务能力并消除单点故障。负载均衡支持亿级连接和千万级并发,可轻松应对大流量访问,满足业务需求。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档