Nginx下限速限制下载速度实例

转载:http://www.cuplayer.com/player/PlayerCode/Nginx/2014/0917/1571.html

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

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

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

 
  
  1. location /flv/ { 
  2.     limit_rate_after 500k; 
  3.     limit_rate       50k; 

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

 
  
  1. static ngx_int_t 
  2. ngx_http_write_filter_init(ngx_conf_t *cf) 
  3.     ngx_http_top_body_filter = ngx_http_write_filter
  4.   
  5.     return NGX_OK; 

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

 
  
  1. if (r->limit_rate) { 
  2.     limit = r->limit_rate * (ngx_time() - r->start_sec + 1) 
  3.             - (c->sent - clcf->limit_rate_after); 
  4.  
  5.     if (limit <= 0) { 
  6.         c->write->delayed = 1
  7.         ngx_add_timer(c->write, 
  8.                       (ngx_msec_t) (- limit * 1000 / r->limit_rate + 1)); 
  9.  
  10.         c->buffered |= NGX_HTTP_WRITE_BUFFERED; 
  11.  
  12.         return NGX_AGAIN; 
  13.     } 
  14.  
  15.     if (clcf->sendfile_max_chunk 
  16.         && (off_t) clcf->sendfile_max_chunk < limit
  17.     { 
  18.         limit = clcf->sendfile_max_chunk; 
  19.     } 
  20.  

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

  1. 当c->sentlimit_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对这些细节上的处理非常仔细啊,保证一个准确度。

 
  
  1. if (r->limit_rate) { 
  2.  
  3.     nsent = c->sent; 
  4.  
  5.     if (clcf->limit_rate_after) { 
  6.  
  7.         sent -clcf->limit_rate_after; 
  8.         if (sent < 0) { 
  9.             sent = 0
  10.         } 
  11.  
  12.         nsent -clcf->limit_rate_after; 
  13.         if (nsent < 0) { 
  14.             nsent = 0
  15.         } 
  16.     } 
  17.  
  18.     delay = (ngx_msec_t) ((nsent - sent) * 1000 / r->limit_rate); 
  19.  
  20.     if (delay > 0) { 
  21.         limit = 0
  22.         c->write->delayed = 1
  23.         ngx_add_timer(c->write, delay); 
  24.     } 

接下来nginx还做了点延时的微调,不过这个是涉及到sendfile_max_chunk指令,而不是limit_rate指令的,所以不做分析。

 
  
  1. if (limit 
  2.     && c->write->ready 
  3.     && c->sent - sent >= limit - (off_t) (2 * ngx_pagesize)) 
  4.     c->write->delayed = 1
  5.     ngx_add_timer(c->write, 1); 

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

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

你可能感兴趣的:(nginx)