ngonx限流(ngnix如何做限流)

本文主要介绍ngonx限流(如何通过ngnix限制电流),下面一起看看ngonx限流(如何通过ngnix限制电流)相关资讯。
1.电流限制算法ngx_http_limit_req_module使用桶算法来定义桶。当桶的出口速度固定时,请求被缓存在桶中,桶的体积被定义。当请求超过容量时,该请求将被丢弃。
limit_req_zone:定义一个桶,定义桶的出口流量。limit_req:定义使用桶和桶的体积。
使用示例
2.源代码分析2.1 ngx_http_limit_req_zone保存bucket属性,申请分配共享块。
43 typedef struct { 44 ngx _ http _ limit _ req _ shctx _ t * sh;//共享块上的bucket 45 ngx _ slab _ pool _ t * sh pool;//共享块内存池46/*整数值,1对应0.001r/s */47ngx _ uint _ t速率;//枪管出口速度48 ngx _ http _ complex _ value _ t key//桶的节点键值是49 ngx _ http _ limit _ req _ node _ t * node;//最近命中该桶请求的节点//这里有并发吗?50 } ngx _ http _ limit _ req _ ctx _ t;863 static char * 864 ngx _ http _ limit _ req _ zone(ngx _ conf _ t * cf,ngx_command_t *cmd,void * conf)865 { 873 ngx _ http _ limit _ req _ ctx _ t * ctx;970 ctx-rate = rate * 1000 /规模;//流量精度毫秒级971 972 shm _ zone = ngx _ shared _ memory _ add(cf,name,size,973 ngx _ http _ limit _ req _ module);986 987 shm _ zone-init = ngx _ http _ limit _ req _ init _ zone;988 shm _ zone-data = ctx;989 990返程ngx _ conf _ ok;991}共享块上有一棵红黑树和一棵队列红黑树,用于根据键快速找到请求节点为lru队的队列。列,用于回收最长时间没有使用的内存,防止共享块被耗尽。
722静态ngx _ int _ t 723 ngx _ http _ limit _ req _ init _ zone(ngx _ shm _ zone _ t * shm _ zone,void * data)724 { 732 if(octx){ 746 ctx-sh = octx-sh;747 ctx-sh pool = octx-sh pool;748 749返回ngx _ ok750 } 762 ctx-sh = ngx _ slab _ alloc(ctx-sh pool,sizeof(ngx _ http _ limit _ req _ shctx _ t));763 if(ctx-sh = = null){ 764 return ngx _ error;765 } 766 767 ctx-sh pool-data = ctx-sh;768 769 ngx _ rb tree _ init(ctx-sh-rb tree,ctx-sh-sentinel,770 ngx _ http _ limit _ req _ rb tree _ insert _ value);771 772 ngx _ queue _ init(ctx-sh-queue);785 786返回ngx _ ok787}可以看出,bucket是由http{}块定义的,bucket无法区分传递给bucket的节点需要包含哪个服务器{}或者哪个位置{}的信息,比如主机uri remote_ip。
2.2 limit_req使用限制器用户在限制电流的位置使用limit_req指令,并将限制器添加到ngx _ http _ limit _ req _ conf _ t-limits数组中。
61 typedef struct { 62/* ngx _ http _ limit _ req _ limit _ t * */63//当前位置的限制器64 ngx_array_t限制;65 ngx _ uint _ t limit _ log _ level66 ngx _ uint _ t delay _ log _ level67 ngx_uint_t状态码;68 ngx_flag_t干跑;69 } ngx_http _ limit _ req _ conf _ t;999 static char * 1000 ngx _ http _ limit _ req(ngx _ conf _ t * cf,ngx_command_t *cmd,void * conf)1001 { 1002 ngx _ http _ limit _ req _ conf _ t * lrcf = conf;1023 shm _ zone = ngx _ shared _ memory _ add(cf,s,0,1024 ngx _ http _ limit _ req _ module);1090 limit = ngx _ array _ push(lrcf-limits);1091 if(limit = = null){ 1092 return ngx _ conf _错误;1093 } 1094 1095 limit-shm _ zone = shm _ zone;1096极限-突发=突发* 1000;1097极限-延迟=延迟* 1000;1098 1099返回ngx _ conf _ ok;1100 }2.3请求处理在ngx_http_preaccess_phase阶段处理。
1122静态ngx _ int _ t 1123 ngx _ http _ limit _ req _ init(ngx _ conf _ t * cf)1124 { 1125 ngx _ http _ handler _ pt * h;1126 ngx _ http _ core _ main _ conf _ t * cmcf;1127 1128 cmcf = ngx _ http _ conf _ get _ module _ main _ conf(cf,ngx _ http _ core _ module);1129 1130h = ngx _ array _ push(cmcf-phases[ngx _ http _ pre access _ phase])。处理者);1131 if(h = = null){ 1132 return ngx _ error;1133 } 1134 1135 * h = ngx _ http _ limit _ req _ handler;1136 1137返回ngx _ ok1138}处理请求,该请求可能被丢弃、延迟和通过。
199静态ngx_int_t 200 ngx _ http _ limit _ req _ handler(ngx _ http _ request _ t * r)201 { 225//使用该位置的有限streamers,检查//是否有桶溢出,跳出for {} //如果所有桶都没有溢出,返回ngx_ok,跳出for {}。n lrcf-limits . nelts;n){ 227 228 limit = limits[n];229 230 ctx = limit-shm _ zone-data;231 232 if(ngx _ http _ complex _ value(r,ctx-key,key)!= ngx _ ok){ 233 ngx _ http _ limit _ req _ unlock(limits,n);234返回ngx _ http _ internal _ server _ error;235 } 236 237 if (key.len == 0) { 238继续;239 } 240 241 if(key . len 65535){ 242 ngx _ log _ error(ngx _ log _ err,r-connection-log,0,243 \ % v \ 钥匙和钥匙244 大于65535 byt: \ % v \ ,245 ctx-key.value,key);246继续;247 } 248 249 hash = ngx _ crc32 _ short(key . data,key . len);250 251 ngx _ sh mtx _ lock(ctx-sh pool-mut:通过所有限幅器255 * ngx _再次:通过限幅器,检查下一个限幅器256 * ngx _ busy :触发限制,需要丢弃257 * ngx _ :错误,需要丢弃258 */259 rc = ngx _ http _ limit _ req _ lookup(limit,hash,key,excess,260(n = = lrcf-limits . nelts-1));261 262 ngx _ sh mtx _ unlock(ctx-sh pool-mutex);267 268 if (rc!= ngx _ again){ 269 break;270 } 271 } 273 if(rc = = ngx _ declined){ 274 return ngx _ declined;275} 276 //请求溢出直接丢弃277 if(rc = = ngx _ busy | rc = = ngx _ error){ 278 287 ngx _ http _ limit _ req _ unlock(limits,n);288 289 if(lrcf-dry _ run){ 290 r-main-limit _ req _ status = ngx _ http _ limit _ req _ rejected _ dr y _ run;291返回ngx _谢绝;292 } 293 294 r-main-limit _ req _ status = ngx _ http _ limit _ req _ rejected;295 296返回lrcf-status _ code;297 } 299/* rc = = ngx _ again | | rc = = ngx _ ok */300 301 if(rc = = ngx _ again){ 302 excess = 0;303} 304 //请求不溢出。检查是否需要延时处理(当吊桶出口流量小于入口流量时)305 delay = ngx _ http _ limit _ req _ account(limits,n,excess,limit);306 //唐 t不需要延迟处理,交给下一个模块307如果(!delay){ 308 r-main-limit _ req _ status = ngx _ http _ limit _ req _ passed;309返回ngx _谢绝;310} //延迟处理333 r-read _ event _ handler = ngx _ http _ test _ reading;334 r-write _ event _ handler = ngx _ http _ limit _ req _ delay;335 336 r-连接-写入-延迟= 1;337 ngx _ add _ timer(r-连接-写,延时);338 339再返回ngx _一次;340}检查请求是否溢出。
415静态ngx _ int _ t 416 ngx _ http _ limit _ req _ lookup(ngx _ http _ limit _ req _ limit _ t * limit,ngx_uint_ t hash,417 ngx_str_t *key,ngx_uint_t *ep,ngx _ uint _ t account)418 { 427 now = ngx _ current _ msec;428 429 ctx = limit-shm _ zone-data;430 431 node = ctx-sh-rb tree . root;432 sentinel = ctx-sh-rb tree . sentinel;433 //遍历树434 while(节点!= sentinel) { 435 436 if(哈希node-key){ 437 node = node-left;438继续;439 } 440 441 if(哈希node-key){ 442 node = node-right;443继续;444 } 445 446/* hash = = node-key */447//found node 448 lr =(ngx _ http _ limit _ req _ node _ t *)node-color;449 450 rc = ngx_memn2cmp(key-data,lr-data,key-len,(size _ t)lr-len);451 452 if (rc == 0) {//当请求命中树时,将节点移动到lru队列的头部,将lru队列的尾部保留为最长时间未被访问的节点453//将最近访问的节点移动到lru队列的头部454 ngx _ queue _ remove (lr队列);455 ngx _ queue _ insert _ head(ctx-sh-queue,lr-queue);456 //计算桶容量消耗457 ms =(ngx _ msec _ int _ t)(now-lr-last);458 459 if(ms-60000){ 460 ms = 1;461 462 } else if(ms 0){ 463 ms = 0;464 } 466//ctx-rat:可访问数467//1000 :消耗的访问数468//lr-超额:溢出数之和469 //超额:最后溢出数470超额= lr-超额-ctx-rate * ms/1000 1000;471 472 if(超额0) { 473超额= 0;474 } 475 476 *ep =超额;477 //当消耗大于桶的容积时溢出478//req溢出479 if((ngx _ uint _ t)excess limit-burst){ 480 return ngx _ busy;481} 482 //不超过枪管容积。//如果有其他限流器,再次返回ngx _检查下一个限流器。//如果是最后一个限流器,返回ngx _ ok 483//通过最后一个限流器,更新时间并超过484//返回ngx _ ok 485if (account)。487 488 if(ms){ 489 lr-last = now;490 } 491 492返回ngx _ ok493} //标记这个节点,表示这个节点刚刚被访问过495//增加访问次数496 lr-count;497 498 ctx-node = lr;499 500又返回ngx _了;501 } 502 503节点= (rc 0)?节点-左:节点-右;504} //如果树没有命中,添加新节点506 //添加新节点507 508 * ep = 0;509 510 size = offset of(ngx _ rb tree _ node _ t,color)511 offset of(ngx _ http _ limit _ req _ node _ t,data)512 key-len;513 //恢复长期不用的节点514//恢复的mem 515 ngx _ http _ limit _ req _ expire(ctx,1);516 517 node = ngx _ slab _ alloc _ locked(ctx-sh pool,size);518 519 if(node = = null){ 520 ngx _ http _ limit _ req _ expire(ctx,0);521 522 node = ngx _ slab _ alloc _ locked(ctx-sh pool,size);523 if(node = = null){ 524 ngx _ log _ error(ngx _ log _ alert,ngx_cycle-log,0,525 无法分配节点% s ,ctx-sh pool-lo g _ ctx);526返回ngx _ error527 } 528 } 529 530 node-key =哈希;531 532 lr =(ngx _ http _ limit _ req _ node _ t *)node-color;533 534 lr-len =(u _ short)key-len;535 lr-超额= 0;536 537 ngx_memcpy(lr-data,key-data,key-len);538 539 ngx _ rb tree _ insert(ctx-sh-rb tree,node);540 541 ngx _ queue _ insert _ head(ctx-sh-queue,lr-queue);542 543如果(根据nt){ 544 lr-last = now;545 lr-count = 0;546返回ngx _ ok547 } 548 549 lr-last = 0;550 lr-count = 1;551 552 ctx-node = lr;553 554再返回ngx _一次;555}回收最长时间未访问的节点。
655静态void 656 ngx _ http _ limit _ req _ expire(ngx _ http _ limit _ req _ ctx _ t * ctx,ngx _ uint _ t n)657 { 665 now = ngx _ current _ msec;666 667 /* 668 * n == 1删除一个或两个零速率条目669 * n == 0强制删除最旧的条目670 *和一个或两个零速率条目671 */672//n = = 1 :回收超过60秒的无请求命中,桶缓存中最多有两个节点。//n = = 0 :强行回收一个,然后按n = = 1,再两个673而(n ^ 3){ 675 if(ngx _ queue _ empty(ctx-sh-queue))可能被回收。677} 678 //获取最老的节点679//getoldestnode 680 q = ngx _ queue _ last(ctx-sh-queue);681 682 lr = ngx_queue_data(q,ngx_http_limit_req_node_t,queue);683 //如果lr-count0表示该节点最近被命中,则意味着所有节点都不应该被回收。684 if (lr-count) {685 686/* 687 *进一步查找没有太大意义,688 *因为我们在查找阶段撞节点。689 */690 691返回;692 } // n!= 0,检查此节点是否可以避免回收694如果(n!= 0){ 695 696 ms =(ngx _ msec _ int _ t)(now-lr-last);697 ms = ngx_abs(毫秒);698 699 if(ms 60000){ 700 return;701 } 702 703超额= lr-超额-ctx-费率* ms/1000;704 705 if(超出0){ 706 return;707} 708} //回收节点710 ngx _ queue _ remove(q);711 712 node =(ngx _ rb tree _ node _ t *)713((u _ char *)lr-offset of(ngx _ rb tree _ node _ t,color));714 715 ngx _ rb tree _ delete(ctx-sh-rb tree,node);716 717 ngx _ slab _ free _ locked(ctx-sh pool,node);718} 719}检查请求是否延迟。
558静态ngx _ msec _ t 559 ngx _ http _ limit _ req _ account(ngx _ http _ limit _ req _ limit _ t * limits,ngx_uin t_t n,560 ngx_uint_t *ep,ngx _ http _ limit _ req _ limit _ t * * limit)561 {//ep是最后一个限流器的延时结果,568 excess = * ep569 570 if((ngx _ uint _ t)excess =(* limit)-delay){ 571 max _ delay = 0;572 573 } else { 574 ctx =(* limit)-shm _ zone-data;575 max_delay =(超额-(*限额)-延迟)* 1000/ctx-rate;576} //遍历有限流器,找到578的最大延迟值while(n-){///当ngx_http_limit_req_lookup,这个请求对应的红黑树节点指针保存在ctx-node,//都可以直接获取,不需要遍历。节点。579 ctx =限值[n]。shm _ zone-data;580 lr = ctx-node;581 582 if (lr == null) { 583继续;584 } 585 586 ngx _ sh mtx _ lock(ctx-sh pool-mutex);587 588 now = ngx _ current _ msec589 ms =(ngx _ msec _ int _ t)(now-lr-last);590 591 if(ms-60000){ 592 ms = 1;593 594 } else if(ms 0){ 595 ms = 0;596 } 597 598超额= lr-超额-ctx-费率* ms/1000 1000;599 600 if(超额0) { 601超额= 0;602 } 603 604 if(ms){ 605 lr-last = now;606 } 607 608 lr-超额=超额;609 lr-count-;610 611 ngx _ sh mtx _ unlock(ctx-sh pool-mutex);612 613 ctx-node = null;614 615 if((ngx _ uint _ t)excess = limits[n]。延迟){ 616继续;617 } 618 619延迟=(超出限制[n]。延时)* 1000/ctx-rate;620 621 if(delay max _ delay){ 622 max _ delay = delay;623 *ep =超额;624 * limit = limits[n];625 } 626 } 627 628 return max _ delay629 }3.本模块展示了共享块的使用。因为共享块是全局的(不可能根据配置上下文区分(服务器{位置{})),所以来自服务器{}位置{}的所有数据都应该记录在共享块的容器中。为了区分这些数据,必须使用密钥。
为了有效地回收共享块内存,使用了lru链表l。ru链表是把最近命中的节点放在链表的头部,那么链表的尾部就是最长时间没有使用的节点,所以不应该释放。
ngx 红黑树中节点的使用
ngx _ http _ limit _ req _ node _ t-data[1]是一个灵活数组,所以分配的空间是数组长度为key-len。
ngx在计算请求是否过载方面也很聪明。
470超额= lr-超额-ctx-费率* ms/1000 1000;lr-excessiveness是桶的当前剩余容量ctx-速率* ms/1000 :桶出口流速*时间=在这段时间内桶的增加容量1000在:单次访问所消耗的容量。所以最终得到这个请求后,桶的剩余容量就会溢出,被丢弃。如果容量大于0,则延迟处理,如果容量小于0,则立即处理。
标签:
节点速度
了解更多ngonx限流(如何通过ngnix限制电流)相关内容请关注本站点。