Nginx限速模块初探(4)

LRU算法的实现很简单,如果一个节点被访问了,那么就把它移到队列的头部,当空间不足需要淘汰节点时,就选出队列尾部的节点淘汰掉,主要体现在如下代码中:

ngx_queue_remove(&lr->queue); // 2. 修改该点在LRU队列中的位置,表示该点最近被访问过 ngx_queue_insert_head(&ctx->sh->queue, &lr->queue);// 2 ... ngx_http_limit_req_expire(ctx, 1); // 4. 根据LRU淘汰,腾出空间

漏桶算法是如何实现的

漏桶算法的实现也比我们想象的简单,其核心是这一行公式excess = lr->excess - ctx->rate * ngx_abs(ms) / 1000 + 1000,这样代码的意思是:excess表示当前key上遗留的请求数,本次遗留的请求数 = 上次遗留的请求数 - 预设速率 X 过去的时间 + 1。这个1表示当前这个请求,由于Nginx内部表示将单位缩小了1000倍,所以1个请求要转换成1000。

excess = lr->excess - ctx->rate * ngx_abs(ms) / 1000 + 1000; // 3. 执行漏桶算法 if (excess < 0) excess = 0; if ((ngx_uint_t) excess > limit->burst) return NGX_BUSY; // 超过了突发门限,拒绝 if (account) { // 是否是最后一条规则 lr->excess = excess; lr->last = now; return NGX_OK; // 未超过限制,通过 } ... return NGX_AGAIN; // 6. 执行下一条限流规则

上述代码受限算出当前key上遗留的请求数,如果超过了burst,就直接拒绝;由于Nginx允许多条限速规则同时起作用,如果已是最后一条规则,则允许通过,否则执行下一条规则。

单个key相关的burst队列在哪里

没有单个key相关的burst队列。上面代码中我们看到当到达最后一条规则时,只要excess<limit->burst限速模块就会返回NGX_OK,并没有把多余请求放入队列的操作,这是因为Nginx是基于timer来管理请求的,当限速模块返回NGX_OK时,调度函数会计算一个延迟处理的时间,同时把这个请求放入到共享的timer队列中(一棵按等待时间从小到大排序的红黑树)。

ngx_http_limit_req_handler(ngx_http_request_t *r) { ... for (n = 0; n < lrcf->limits.nelts; n++) { ... ngx_shmtx_lock(&ctx->shpool->mutex);// 获取锁 rc = ngx_http_limit_req_lookup(limit, hash, &key, &excess, // 执行漏桶算法 (n == lrcf->limits.nelts - 1)); ngx_shmtx_unlock(&ctx->shpool->mutex);// 释放锁 ... if (rc != NGX_AGAIN) break; } ... delay = ngx_http_limit_req_account(limits, n, &excess, &limit);// 计算当前请求需要的延迟时间 if (!delay) { return NGX_DECLINED;// 不需要延迟,交给后续的handler进行处理 } ... ngx_add_timer(r->connection->write, delay);// 否则将请求放到定时器队列里 return NGX_AGAIN; // the request has been successfully processed, the request must be suspended until some event. }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/589a28b238089c99ded105edb8e6c3f9.html