从构建分布式秒杀系统聊聊限流特技

时间:2019-07-15 来源:www.anime669.com

日博官网

作者:小齐2012

前言

俗话说,三英尺的冰并不是白天的冷,水的滴水不是一天的工作。罗马不是一天建成的。两秒钟之前,秒杀案最初成立并与中国最大的同性交友网站Code Cloud共享。与此同时,我也收到了小伙伴的许多建议和投诉。我从不认为分布式,集群和尖峰应该是大型制造商的专利。我今天必须随时在互联网上武装自己。只有这样,也许你的春天就是明天。

在开发尖峰系统案例的过程中,主要的是共享队列,缓存,锁和分布式锁,以及静态。缓存的目的是提高系统访问速度,增强系统的处理能力;分布式锁解决了集群下数据安全一致性的问题;静态化无疑会降低缓存和数据库层的压力。

限流

但是,经过重新优化的设计,我们还对特殊场景进行了特殊处理。采取尖峰,可能有数百万用户抢购,产品数量远远小于用户数量。如果这些请求排队或查询缓存,则最终结果中没有任何意义,并且后台数据非常漂亮。为了减少资源浪费和降低后端压力,我们还需要限制峰值,只是为了确保一些用户正常。

就尖峰接口而言,当访问频率或并发请求超出其容差范围时,我们应该考虑当前限制以确保接口的可用性,以防止系统因意外的系统压力而瘫痪。通常的策略是拒绝冗余访问或排队等待服务的冗余访问。

限流算法

Any current limiting is not a purposeless, nor a problem that can be solved by a switch. Commonly used current limiting algorithms are: token bucket, leaky bucket.

Token bucket

The token bucket algorithm is one of the most commonly used algorithms in Traffic Shaping and Rate Limiting. Typically, the token bucket algorithm is used to control the number of data sent to the network and to allow the transmission of bursty data (encyclopedia).

62619176cc564adf8c95d88236eb4176

In the spike activity, the user's request rate is not fixed. Here we assume 10r/s, the token is put into the token bucket at a rate of 5 per second, and the bucket can store up to 20 tokens. Think about it, is there always a part of the request to be discarded.

Leaky barrel

The main purpose of the leaky bucket algorithm is to control the rate at which data is injected into the network and smooth burst traffic on the network. The leaky bucket algorithm provides a mechanism by which burst traffic can be shaped to provide a stable flow (encyclopedia) for the network.

4c3f937948154ce9be39e6b852ed8b20

The token bucket is no matter how much you flow in, I will process it at the established rate, and if the bucket is full, it will refuse service.

Apply current limit

Tomcat

In the Tomcat container, we can achieve the purpose of current limiting by customizing the thread pool, configuring the maximum number of connections, and requesting processing queues and other parameters.

309f6378e42c46dea125fd4147849849

Tomcat uses its own connection pool by default. Here we can also customize the implementation. Open the /conf/server.xml file and configure a thread pool before the Connector:

694491974c1e405388a6f11b25164485

xx名称:共享线程池的名称。这是Connector为了共享线程池而引用的名称。名称必须是唯一的。默认值:无; namePrefix:在JVM上,每个正在运行的线程都可以有一个名称字符串。此属性为线程池中每个线程的名称字符串设置前缀,Tomcat将线程编号附加到前缀的末尾。默认值:tomcat-exec-; maxThreads:此线程池可容纳的最大线程数。默认值:200; maxIdleTime:在Tomcat关闭空闲线程之前允许空闲线程持续的时间量(以毫秒为单位)。仅当当前活动线程的数量大于minSpareThread的值时,才会关闭空闲线程。默认值:60000(一分钟)。 minSpareThreads:Tomcat应始终打开的最小非活动线程数。默认值:25。

配置连接器

2ef6f4ecca744c3196e2d18213adfd51

Executor:表示与参数值对应的线程池; minProcessors:处理服务器启动时创建的请求的线程数; maxProcessors:可以创建以处理请求的最大线程数; acceptCount:指定可用于所有处理请求的线程数。使用all时,将不会处理可放入处理队列的请求数。

API电流限制

在尖峰活动中,来自接口的请求数量将是数百甚至数千次,这可能导致接口不可用,导致连锁反应导致整个系统崩溃,甚至可能影响其他服务。

那么如何应对这种突然事件呢?这里我们采用开源工具包番石榴提供的限流工具类RateLimiter进行API限流,该类基于 '令牌桶算法',开箱即用。

自定义定义注解

023e72805d0849efb24dea14ab76f070

自定义切面

63643ea07b11418e84b088c5d13377b8

业务实现:

237045f434c74d9598d26212bb867a0e

分布式限流

nginx的

如何使用Nginx的实现基本的限流,比如单个IP限制每秒访问50次。通过Nginx的限流模块,我们可以设置一旦并发连接数超过我们的设置,将返回503错误给客户端。

配置nginx.conf

ca57067c647045af98d6ff37642376f7

配置说明

imit_conn_zone

是针对每个IP定义一个存储会话状态的容器。这个示例中定义了一个百米的容器,按照32字节/会话,可以处理320万个会话。

limit_rate 300k;

对每个连接限速300K。注意,这里是对连接限速,而不是对IP限速。如果一个IP允许两个并发连接,那么这个IP就是限速limit_rate×2。

突发=5;

XX这相当于桶的大小。如果请求超过系统的处理速度,它将被放入存储桶并等待处理。如果存储桶已满,那么抱歉,请求直接返回503,客户端从服务器获得忙响应。如果系统缓慢处理请求,则桶中的请求不能保留在其中。如果超过一定时间,将直接返回,返回服务器的忙响应。

OpenResty

6731b4b846414be3a1898cfe0530a26b

无论背后是否熟悉,这都是老罗万岁的直接了解,2015罗洛在锤技术T2大会上向OpenResty捐赠了门票收入,并相信老罗是一个多愁善感的胖子。

这里我们使用OpenResty开源限流解决方案,测试用例采用最新版本的OpenResty1.13.6.1,自带lua-resty-limit-traffic模块和case,实现起来比较方便。

限制接口的并发/请求总数

在尖峰活动中,由于流量的突然增加,它可能会影响整个系统的稳定性并导致崩溃。此时,我们需要限制spike接口的并发/请求总数。

这里我们在lua-resty-limit-traffic中使用resty.limit.count模块,请参阅文章特定长度的源代码openresty/lua/limit_count.lua。

限制接口时间窗口请求的数量

在秒杀场景中,有时候它是一个人类的肉体鼠标,如12306抢票软件,软件刷票可以比人肉鼠标快得多。此时我们必须限制每个客户端的请求数量,这样票证就不那么尴尬了。当然,道路的高度是一英尺高,票务软件总是有办法规避你的防守。另一方面,它也促进了技术的进步。

这里我们在lua-resty-limit-traffic中使用resty.limit.conn模块。有关具体代码,请参阅源代码openresty/lua/limit_conn.lua。

平滑限制接口请求编号

先前的限流方法允许突发流量,这意味着允许瞬时流量。如果不受限制,突然流量将影响整个系统的稳定性。因此,在尖峰情形中,需要将请求整形为平均速率处理,即20r/s。

这里我们使用lua-resty-limit-traffic中的resty.limit.req模块来实现漏桶限流和令牌桶电流限制。

实际上,漏桶和令牌桶之间的根本区别在于如何处理超过请求率的请求。漏桶将请求进入队列等待平均速度处理,队列满了将拒绝该服务;如果桶容量允许,令牌桶将直接处理突发请求。

漏桶

铲斗容量大于零并且是延迟模式。如果存储桶未满,则进入请求队列并等待以固定速率处理,否则请求被拒绝。

令牌桶

铲斗容量大于零并且是非延迟模式。如果存储桶中有令牌,则允许突发流量,否则请求被拒绝。

压力测量

为了测试上面的配置效果,我们使用AB压力测试,在Linux下执行以下命令:

8c5bd1dad9cd4f3b9c275a49a4b7dca1

测试命令:

b0538003a9ab4a349cd64fc4cb30b8f0

测试结果:

8f0694008edd4fdca26c1c7118fecad7

总结

有了上限流程,只需对这个尖峰情况做一个简单的总结,每个人都不应该刻意区分方案的质量,只要它适合业务场景是最好的。

源代码: