当前位置 主页 > 服务器问题 > nginx问题汇总 >

    Nginx源码研究之nginx限流模块详解

    栏目:nginx问题汇总 时间:2019-03-21 16:51

    这篇文章主要介绍了Nginx源码研究之nginx限流模块详解,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

    高并发系统有三把利器:缓存、降级和限流;

    限流的目的是通过对并发访问/请求进行限速来保护系统,一旦达到限制速率则可以拒绝服务(定向到错误页)、排队等待(秒杀)、降级(返回兜底数据或默认数据);

    高并发系统常见的限流有:限制总并发数(数据库连接池)、限制瞬时并发数(如nginx的limit_conn模块,用来限制瞬时并发连接数)、限制时间窗口内的平均速率(nginx的limit_req模块,用来限制每秒的平均速率);

    另外还可以根据网络连接数、网络流量、CPU或内存负载等来限流。

    1.限流算法

    最简单粗暴的限流算法就是计数器法了,而比较常用的有漏桶算法和令牌桶算法;

    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;

    2.nginx基础知识

    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;

    typedef enum { NGX_HTTP_POST_READ_PHASE = 0, //目前只有realip模块会注册handler(nginx作为代理服务器时有用,后端以此获取客户端原始ip)  NGX_HTTP_SERVER_REWRITE_PHASE, //server块中配置了rewrite指令,重写url  NGX_HTTP_FIND_CONFIG_PHASE, //查找匹配location;不能自定义handler; NGX_HTTP_REWRITE_PHASE,  //location块中配置了rewrite指令,重写url NGX_HTTP_POST_REWRITE_PHASE, //检查是否发生了url重写,如果有,重新回到FIND_CONFIG阶段;不能自定义handler;  NGX_HTTP_PREACCESS_PHASE,  //访问控制,限流模块会注册handler到此阶段  NGX_HTTP_ACCESS_PHASE,  //访问权限控制 NGX_HTTP_POST_ACCESS_PHASE, //根据访问权限控制阶段做相应处理;不能自定义handler;  NGX_HTTP_TRY_FILES_PHASE,  //只有配置了try_files指令,才会有此阶段;不能自定义handler; NGX_HTTP_CONTENT_PHASE,  //内容产生阶段,返回响应给客户端  NGX_HTTP_LOG_PHASE   //日志记录} ngx_http_phases;