nginx限流模块源码分析


这篇文章主要介绍“nginx限流模块源码分析”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“nginx限流模块源码分析”文章能帮助大家解决问题。高并发系统有三把利器:缓存、降级和限流;限流的目的是通过对并发访问/请求进行限速来保护系统,一旦达到限制速率则可以拒绝服务(定向到错误页)、排队等待(秒杀)、降级(返回兜底数据或默认数据);高并发系统常见的限流有:限制总并发数(数据库连接池)、限制瞬时并发数(如nginx的limit_conn模块,用来限制瞬时并发连接数)、限制时间窗口内的平均速率(nginx的limit_req模块,用来限制每秒的平均速率);另外还可以根据网络连接数、网络流量、cpu或内存负载等来限流。最简单粗暴的限流算法就是计数器法了,而比较常用的有漏桶算法和令牌桶算法;1.1计数器计数器法是限流算法里最简单也是最容易实现的一种算法。比如我们规定,对于a接口来说,我们1分钟的访问次数不能超过100个。那么我们我们可以设置一个计数器counter,其有效时间为1分钟(即每分钟计数器会被重置为0),每当一个请求过来的时候,counter就加1,如果counter的值大于100,就说明请求数过多;这个算法虽然简单,但是有一个十分致命的问题,那就是临界问题。如下图所示,在1:00前一刻到达100个请求,1:00计数器被重置,1:00后一刻又到达100个请求,显然计数器不会超过100,所有请求都不会被拦截;然而这一时间段内请求数已经达到200,远超100。1.2 漏桶算法如下图所示,有一个固定容量的漏桶,按照常量固定速率流出水滴;如果桶是空的,则不会流出水滴;流入到漏桶的水流速度是随意的;如果流入的水超出了桶的容量,则流入的水会溢出(被丢弃);可以看到漏桶算法天生就限制了请求的速度,可以用于流量整形和限流控制;1.3 令牌桶算法令牌桶是一个存放固定容量令牌的桶,按照固定速率r往桶里添加令牌;桶中最多存放b个令牌,当桶满时,新添加的令牌被丢弃;当一个请求达到时,会尝试从桶中获取令牌;如果有,则继续处理请求;如果没有则排队等待或者直接丢弃;可以发现,漏桶算法的流出速率恒定或者为0,而令牌桶算法的流出速率却有可能大于r;nginx主要有两种限流方式:按连接数限流(ngx_http_limit_conn_module)、按请求速率限流(ngx_http_limit_req_module);学习限流模块之前还需要了解nginx对http请求的处理过程,nginx事件处理流程等;2.1http请求处理过程nginx将http请求处理流程分为11个阶段,绝大多数http模块都会将自己的handler添加到某个阶段(其中有4个阶段不能添加自定义handler),nginx处理http请求时会挨个调用所有的handler;nginx使用结构体ngx_module_s表示一个模块,其中字段ctx,是一个指向模块上下文结构体的指针;nginx的http模块上下文结构体如下所示(上下文结构体的字段都是一些函数指针):以ngx_http_limit_req_module模块为例,postconfiguration方法简单实现如下:2.2 nginx事件处理简单介绍假设nginx使用的是epoll。nginx需要将所有关心的fd注册到epoll,添加方法生命如下:方法第一个参数是ngx_event_t结构体指针,代表关心的一个读或者写事件;nginx为事件可能会设置一个超时定时器,从而能够处理事件超时情况;定义如下:一般都会循环调用epoll_wait监听所有fd,处理发生的读写事件;epoll_wait是阻塞调用,最后一个参数timeout是超时时间,即最多阻塞timeout时间如果还是没有事件发生,方法会返回;nginx在设置超时时间timeout时,会从上面说的记录超时定时器的红黑树中查找最近要到时的节点,以此作为epoll_wait的超时时间,如下面代码所示;同时nginx在每次循环的最后,会从红黑树中查看是否有事件已经过期,如果过期,标记timeout=1,并调用事件的handler;nginx就是通过上面的方法实现了socket事件的处理,定时事件的处理;ngx_http_limit_req_module模块解析ngx_http_limit_req_module模块是对请求进行限流,即限制某一时间段内用户的请求速率;且使用的是令牌桶算法;3.1配置指令ngx_http_limit_req_module模块提供一下配置指令,供用户配置限流策略注意:$binary_remote_addr是nginx提供的变量,用户在配置文件中可以直接使用;nginx还提供了许多变量,在ngx_http_variable.c文件中查找ngx_http_core_variables数组即可:3.2源码解析ngx_http_limit_req_module在postconfiguration过程会注册ngx_http_limit_req_handler方法到http处理的ngx_http_preaccess_phase阶段;ngx_http_limit_req_handler会执行漏桶算法,判断是否超出配置的限流速率,从而进行丢弃或者排队或者通过;当用户第一次请求时,会新增一条记录(主要记录访问计数、访问时间),以客户端ip地址(配置$binary_remote_addr)的hash值作为key存储在红黑树中(快速查找),同时存储在lru队列中(存储空间不够时,淘汰记录,每次都是从尾部删除);当用户再次请求时,会从红黑树中查找这条记录并更新,同时移动记录到lru队列首部;3.2.1数据结构limit_req_zone配置限流算法所需的存储空间(名称及大小),限流速度,限流变量(客户端ip等),结构如下:limit_req配置限流使用的存储空间,排队队列大小,是否紧急处理,结构如下:前面说过用户访问记录会同时存储在红黑树与lru队列中,结构如下:思考1:ngx_http_limit_req_node_t记录通过prev和next指针形成双向链表,实现lru队列;最新访问的节点总会被插入链表头部,淘汰时从尾部删除节点;思考2:限流算法首先使用key查找红黑树节点,从而找到对应的记录,红黑树节点如何与记录ngx_http_limit_req_node_t结构关联起来呢?在ngx_http_limit_req_module模块可以找到以代码:通过分析上面代码,ngx_rbtree_node_s结构体的color与data字段其实是无意义的,结构体的生命形式与最终存储形式是不同的,nginx最终使用以下存储形式存储每条记录;3.2.2限流算法上面提到在postconfiguration过程会注册ngx_http_limit_req_handler方法到http处理的ngx_http_preaccess_phase阶段;因此在处理http请求时,会执行ngx_http_limit_req_handler方法判断是否需要限流;3.2.2.1漏桶算法实现用户可能同时配置若干限流,因此对于http请求,nginx需要遍历所有限流策略,判断是否需要限流;ngx_http_limit_req_lookup方法实现了漏桶算法,方法返回3种结果:ngx_busy:请求速率超出限流配置,拒绝请求;ngx_again:请求通过了当前限流策略校验,继续校验下一个限流策略;ngx_ok:请求已经通过了所有限流策略的校验,可以执行下一阶段;ngx_error:出错举个例子,假如burst配置为0,待处理请求数初始为excess;令牌产生周期为t;如下图所示3.2.2.2lru淘汰策略上一节叩痛算法中,会执行ngx_http_limit_req_expire淘汰一条记录,每次都是从lru队列末尾删除;第二个参数n,当n==0时,强制删除末尾一条记录,之后再尝试删除一条或两条记录;n==1时,会尝试删除一条或两条记录;代码实现如下:3.2.2.3 burst实现burst是为了应对突发流量的,偶然间的突发流量到达时,应该允许服务端多处理一些请求才行;当burst为0时,请求只要超出限流速率就会被拒绝;当burst大于0时,超出限流速率的请求会被排队等待 处理,而不是直接拒绝;排队过程如何实现?而且nginx还需要定时去处理排队中的请求;2.2小节提到事件都有一个定时器,nginx是通过事件与定时器配合实现请求的排队与定时处理;ngx_http_limit_req_handler方法有下面的代码:计算delay的方法很简单,就是遍历所有的限流策略,计算处理完所有待处理请求需要的时间,返回最大值;简单看看可写事件处理函数ngx_http_limit_req_delay的实现4.1测试普通限流1)配置nginx限流速率为1qps,针对客户端ip地址限流(返回状态码默认为503),如下:2)连续并发发起若干请求;3)查看服务端access日志,可以看到22秒连续到达3个请求,只处理1个请求;23秒到达两个请求,第一个请求处理,第二个请求被拒绝xx.xx.xx.xxx – – [22/sep/2018:23:33:22 +0800] “get / http/1.0” 200 612 “-” “apachebench/2.3”
xx.xx.xx.xxx – – [22/sep/2018:23:33:22 +0800] “get / http/1.0” 503 537 “-” “apachebench/2.3”
xx.xx.xx.xxx – – [22/sep/2018:23:33:22 +0800] “get / http/1.0” 503 537 “-” “apachebench/2.3”
xx.xx.xx.xxx – – [22/sep/2018:23:33:23 +0800] “get / http/1.0” 200 612 “-” “apachebench/2.3”
xx.xx.xx.xxx – – [22/sep/2018:23:33:23 +0800] “get / http/1.0” 503 537 “-” “apachebench/2.3”4.2测试burst1)限速1qps时,超过请求会被直接拒绝,为了应对突发流量,应该允许请求被排队处理;因此配置burst=5,即最多允许5个请求排队等待处理;2)使用ab并发发起10个请求,ab -n 10 -c 10 http://xxxxx;3)查看服务端access日志;根据日志显示第一个请求被处理,2到5四个请求拒绝,6到10五个请求被处理;为什么会是这样的结果呢?查看ngx_http_log_module,注册handler到ngx_http_log_phase阶段(http请求处理最后一个阶段);因此实际情况应该是这样的:10个请求同时到达,第一个请求到达直接被处理,第2到6个请求到达,排队延迟处理(每秒处理一个);第7到10个请求被直接拒绝,因此先打印access日志;第2到6个请求米诶秒处理一个,处理完成打印access日志,即49到53秒每秒处理一个;xx.xx.xx.xxx – – [22/sep/2018:23:41:48 +0800] “get / http/1.0” 200 612 “-” “apachebench/2.3”
xx.xx.xx.xxx – – [22/sep/2018:23:41:48 +0800] “get / http/1.0” 503 537 “-” “apachebench/2.3”
xx.xx.xx.xxx – – [22/sep/2018:23:41:48 +0800] “get / http/1.0” 503 537 “-” “apachebench/2.3”
xx.xx.xx.xxx – – [22/sep/2018:23:41:48 +0800] “get / http/1.0” 503 537 “-“ 香港云主机 “apachebench/2.3”
xx.xx.xx.xxx – – [22/sep/2018:23:41:48 +0800] “get / http/1.0” 503 537 “-” “apachebench/2.3”
xx.xx.xx.xxx – – [22/sep/2018:23:41:49 +0800] “get / http/1.0” 200 612 “-” “apachebench/2.3”
xx.xx.xx.xxx – – [22/sep/2018:23:41:50 +0800] “get / http/1.0” 200 612 “-” “apachebench/2.3”
xx.xx.xx.xxx – – [22/sep/2018:23:41:51 +0800] “get / http/1.0” 200 612 “-” “apachebench/2.3”
xx.xx.xx.xxx – – [22/sep/2018:23:41:52 +0800] “get / http/1.0” 200 612 “-” “apachebench/2.3”
xx.xx.xx.xxx – – [22/sep/2018:23:41:53 +0800] “get / http/1.0” 200 612 “-” “apachebench/2.3″4)ab统计的响应时间见下面,最小响应时间87ms,最大响应时间5128ms,平均响应时间为1609ms:4.3测试nodelay1)4.2显示,配置burst后,虽然突发请求会被排队处理,但是响应时间过长,客户端可能早已超时;因此添加配置nodelay,使得nginx紧急处理等待请求,以减小响应时间:2)使用ab并发发起10个请求,ab -n 10 -c 10 http://xxxx/;3)查看服务端access日志;第一个请求直接处理,第2到6个五个请求排队处理(配置nodelay,nginx紧急处理),第7到10四个请求被拒绝xx.xx.xx.xxx – – [23/sep/2018:00:04:47 +0800] “get / http/1.0” 200 612 “-” “apachebench/2.3”
xx.xx.xx.xxx – – [23/sep/2018:00:04:47 +0800] “get / http/1.0” 200 612 “-” “apachebench/2.3”
xx.xx.xx.xxx – – [23/sep/2018:00:04:47 +0800] “get / http/1.0” 200 612 “-” “apachebench/2.3”
xx.xx.xx.xxx – – [23/sep/2018:00:04:47 +0800] “get / http/1.0” 200 612 “-” “apachebench/2.3”
xx.xx.xx.xxx – – [23/sep/2018:00:04:47 +0800] “get / http/1.0” 200 612 “-” “apachebench/2.3”
xx.xx.xx.xxx – – [23/sep/2018:00:04:47 +0800] “get / http/1.0” 200 612 “-” “apachebench/2.3”
xx.xx.xx.xxx – – [23/sep/2018:00:04:47 +0800] “get / http/1.0” 503 537 “-” “apachebench/2.3”
xx.xx.xx.xxx – – [23/sep/2018:00:04:47 +0800] “get / http/1.0” 503 537 “-” “apachebench/2.3”
xx.xx.xx.xxx – – [23/sep/2018:00:04:47 +0800] “get / http/1.0” 503 537 “-” “apachebench/2.3”
xx.xx.xx.xxx – – [23/sep/2018:00:04:47 +0800] “get / http/1.0” 503 537 “-” “apachebench/2.3″4)ab统计的响应时间见下面,最小响应时间85ms,最大响应时间92ms,平均响应时间为88ms:关于“nginx限流模块源码分析”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识,可以关注开发云行业资讯频道,小编每天都会为大家更新不同的知识点。

相关推荐: win7系统如何创建无线连接

这篇文章将为大家详细讲解有关win7系统如何创建无线连接,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。1、首先我们在开始搜索框中输入cmd然后看到cmd.exe后右键以管理员身份进入,因为普通的开始运行cmd没有管理员权限无…

免责声明:本站发布的图片视频文字,以转载和分享为主,文章观点不代表本站立场,本站不承担相关法律责任;如果涉及侵权请联系邮箱:360163164@qq.com举报,并提供相关证据,经查实将立刻删除涉嫌侵权内容。

(0)
打赏 微信扫一扫 微信扫一扫
上一篇 07/17 21:26
下一篇 07/17 21:26

相关推荐