nginx 限速指令limit_rate

最近在学习Nginx(著名的高性能http服务器和反向代理服务器)的模块开发,在此分享nginx的限速实现核心代码。

Nginx的http核心模块ngx_http_core_module中提供limit_rate这个指令可以用于控制速度,limit_rate_after用于设置http请求传输多少字节后开始限速。
另外两个模块ngx_http_limit_conn_modulengx_http_limit_req_module分别用于连接数和连接频率的控制。

限制速度的配置指令简单易懂,限速支持固定的数值

location /flv/ {
    limit_rate_after 500k;
    limit_rate       50k;
}

查看nginx源代码,可以发现ngx_http_write_filter_module.c源文件具体实现了速度的控制,nginx的特点是高度模块化,从名字可以看出这个文件其实也是一个filter模块(nginx中的模块分handler,filter,upstream等三类),这个模块属于filter类别。

static ngx_int_t
ngx_http_write_filter_init(ngx_conf_t *cf)
{
    ngx_http_top_body_filter = ngx_http_write_filter;
 
    return NGX_OK;
}

模块挂载了一个函数在filter的顶端(经过编译链接后此模块即被“压”到filter“链表”的尾部),用于控制数据的输出,这个函数里面就包含了速度的控制。

    if (r->limit_rate) {
        limit = r->limit_rate * (ngx_time() - r->start_sec + 1)
                - (c->sent - clcf->limit_rate_after);
 
        if (limit <= 0) {
            c->write->delayed = 1;
            ngx_add_timer(c->write,
                          (ngx_msec_t) (- limit * 1000 / r->limit_rate + 1));
 
            c->buffered |= NGX_HTTP_WRITE_BUFFERED;
 
            return NGX_AGAIN;
        }
 
        if (clcf->sendfile_max_chunk
            && (off_t) clcf->sendfile_max_chunk < limit)
        {
            limit = clcf->sendfile_max_chunk;
        }
 
    }

上面代码的逻辑是:如果配置文件设置了限速(limit_rate是速度值,size_t类型,0表示不限速)

  1. 当c->sent<clcf->limit_rate_after时,说明还没有到需要限速的阈值,计算limit值大于0(下一次应该传输位置偏移量),不必限速
  2. 当c->sent>clcf->limit_rate_after时,需要控制限速,分两种情况:
    • r->limit_rate * (ngx_time() – r->start_sec + 1)>(c->sent – clcf->limit_rate_after)      理论传输量>实际传输量,不必控制(传得慢了)
    • r->limit_rate * (ngx_time() – r->start_sec + 1)<(c->sent – clcf->limit_rate_after)      理论传输量<实际传输量,需要设置延时(传得快了)
    chain = c->send_chain(c, r->out, limit);

通过上面的c->send_chain函数异步发送数据,nginx在处理完上面send_chain函数后做了延时的微调,倘若进行到下面的程序之前异步IO使得c->sent增加了,则按照增加量添加延时时间delay,因为一般情况这段时间c->sent应该不会来得及改变的。所以如果异步IO改变了数据传输量,也应该及时做速度限制的调整,看得出来nginx对这些细节上的处理非常仔细啊,保证一个准确度。

    if (r->limit_rate) {
 
        nsent = c->sent;
 
        if (clcf->limit_rate_after) {
 
            sent -= clcf->limit_rate_after;
            if (sent < 0) {
                sent = 0;
            }
 
            nsent -= clcf->limit_rate_after;
            if (nsent < 0) {
                nsent = 0;
            }
        }
 
        delay = (ngx_msec_t) ((nsent - sent) * 1000 / r->limit_rate);
 
        if (delay > 0) {
            limit = 0;
            c->write->delayed = 1;
            ngx_add_timer(c->write, delay);
        }
    }
接下来nginx还做了点延时的微调,不过这个是涉及到sendfile_max_chunk指令,而不是limit_rate指令的,所以不做分析。

    if (limit
        && c->write->ready
        && c->sent - sent >= limit - (off_t) (2 * ngx_pagesize))
    {
        c->write->delayed = 1;
        ngx_add_timer(c->write, 1);
    }

总之,可以看出nginx是通过使用ngx_add_timer函数实现对write event的控制,进而实现速度上限的控制。

题外话:nginx对于速度的限制不止是通过limit_rate设置阈值,在upstream模块中通过获取上游服务器返回的响应头headers[“X-Accel-Limit-Rate”]的值也可来动态调整limit_rate的具体数值,这个可以用来实现变化的速度控制。


展开阅读全文

没有更多推荐了,返回首页