使用Nginx实现限流

网上很多类似的文章,但可能没有一些实际压测的说明,这里做简单说明

配置


 #统一在http域中进行配置

 #限制请求

 limit_req_zone $uri zone=api_read:20m rate=50r/s;

 #按ip配置一个连接 zone

 limit_conn_zone $binary_remote_addr zone=perip_conn:10m;

 #按server配置一个连接 zone

 limit_conn_zone $server_name zone=perserver_conn:100m;

===== server =====

location / {

 if (!-e $request_filename){

  rewrite ^/(.*) /index.php last;

 }

 #请求限流排队通过 burst默认是0

 limit_req zone=api_read burst=100;

 #连接数限制,每个IP并发请求为50

 limit_conn perip_conn 50;

 #服务所限制的连接数(即限制了该server并发连接数量)

 limit_conn perserver_conn 200;

 #连接限速

 #limit_rate 100k;

}

压测效果

1. 未限制

1000 个请求并发100 个客户端

image.png

1000 个请求并发1000 个客户端

image.png

并发100、1000,每秒能处理的请求数相近,因为这次目的不是压测nginx 性能,所以没必要继续往下压,这里压测主要是跟后面限流后的数据做对比。

2. 配置限流


rate=50r/s  # 每秒新增50个令牌

burst=100  # 令牌桶一共有100个令牌 

perip_conn 50  # 每个IP最多并发50个连接

perserver_conn 200  # 限制该server并发连接数 

1000 个请求并发100 个客户端

image.png

虽然请求没有失败,但是明显地RPS 下降很明显,请求等待耗时也比不限流要多。

总耗时接近19s,也就是说新增令牌应该是19*50=950,而再加上原来令牌桶有100个令牌,总数是1050个,且perserver_conn=200,按道理也是能够承接100个客户端的1000个请求。

10000 个请求并发100 个客户端

image.png

同上,RPS也是接近50左右,请求没有失败,请求等待耗时跟上面一样

总压测耗时200s,新增令牌数:200*50=10000,没毛病

10000 个请求并发1000 个客户端

image.png

RPS涨到4k多,但是从压测结果来看,接近99%的请求都是失败的,也就是说4k多里面接近99%都是失败的请求

nginx 限制了该server最多只能接受200个并发连接,所以只要nginx接收到的并发数小于200,nginx都能够处理,但由于令牌桶的限制,nginx最多只能同时处理100个请求,其余的请求会进行排队,并且会在每秒内在生成50个令牌提供给排队中的请求。

3. 匹配指定路径进行限流


upstream ykz-www {

 server 10.13.14.134:80;

}

server {

 listen 80;

 server_name www-test.yunkezan.com yunkezan.yaochufa.com www.yunkezan.cn yunkezan.com yunkezan.cn;

 error_log /data/logs/www.yunkezan.com.error.log;

 access_log /data/logs/www.yunkezan.com.access.log main;

 location / {

          proxy_pass http://ykz-www;

          proxy_set_header Host $host;

          proxy_set_header X-Real-IP $remote_addr;

          proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

          proxy_buffer_size 4k;

          proxy_buffers 4 32k;

          proxy_busy_buffers_size 64k;

          proxy_temp_file_write_size 64k; 

 }

 location /front/index/specialDetails {

> > limit_req zone=api_read burst=100;

> > limit_conn perip_conn 50;

                limit_conn perserver_conn 200;

                proxy_pass http://ykz-www;

                proxy_set_header Host $host;

                proxy_set_header X-Real-IP $remote_addr;

                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

                proxy_buffer_size 4k;

                proxy_buffers 4 32k;

                proxy_busy_buffers_size 64k;

                proxy_temp_file_write_size 64k;

 }

 include vhost/common/ssl-yunkezan.com.conf;

}

以上配置可以针对/front/index/specialDetails 该url 下的所有请求进行限流,但是有个问题是:该url 下是产品的链接,而所有产品链接都是在该url下以产品id 进行区分,这样限流相当于将所有产品都进行限流,如果有某个爆款产品把链接占用完了,会影响其他常规产品的访问。

正常一个产品链接:https://www-test.yunkezan.com/front/index/specialDetails?weChatId=421&goodId=17503&activityId=2027&channel=promoteMall&tag_id=-1&tag_name=%E9%99%90%E6%97%B6%E6%8A%A2%E8%B4%AD&tag2_id=-5&tag2_name=%E7%83%AD%E9%97%A8

返回503 被限流。

    #限制请求

    #limit_req_zone $uri zone=api_read:20m rate=50r/s;

    #按ip配置一个连接 zone

    #limit_conn_zone $binary_remote_addr zone=perip_conn:10m;

    #按server配置一个连接 zone

    #limit_conn_zone $server_name zone=perserver_conn:100m;

    #按goodId配置一个连接 zone
image.png