NeedI09in 最近的时间轴更新
NeedI09in's repos on GitHub
Lua · 0 人关注
apisix
The Cloud-Native API Gateway
0 人关注
lua-nginx-module
Embed the Power of Lua into NGINX HTTP servers
0 人关注
lua-resty-core
New FFI-based API for lua-nginx-module
Lua · 0 人关注
lua-resty-healthcheck
Healthcheck library for OpenResty to validate upstream service status
C · 0 人关注
lua-resty-radixtree
Adaptive Radix Trees implemented in Lua / LuaJIT
0 人关注
redsocks
transparent TCP-to-proxy redirector
0 人关注
stream-lua-nginx-module
Embed the power of Lua into NGINX TCP/UDP servers
NeedI09in

NeedI09in

从来就没有什么救世主,人只能自救
V2EX 第 656002 号会员,加入于 2023-10-23 19:15:41 +08:00
今日活跃度排名 11220
NeedI09in 最近回复了
恭喜,钱进账的声音就是好听
72 天前
回复了 gongxuanzhang 创建的主题 程序员 在一个群里被恶心坏了
@Wesson 哈哈哈,有道理。这个解释很有意思,哈哈哈。
72 天前
回复了 doggg 创建的主题 程序员 理解 Nginx 的优雅退出机制
@doggg 👌
72 天前
回复了 doggg 创建的主题 程序员 理解 Nginx 的优雅退出机制
@doggg 嗯嗯,关于进程泄漏的说法应该是设置比较长,然后卡在 timer 那里。
我还遇到过一种进程泄漏,是 worker 里起了 timer ,然后 timer 是一直递归的,发现 reload 后,worker 一直处于 shutting down 。我加了检测 worker.exiting 进程便不泄漏了,现在看来应该是卡在 ngx_process_events_and_timers 里。
72 天前
回复了 doggg 创建的主题 程序员 理解 Nginx 的优雅退出机制
@doggg 我可能没有表述清楚,我是指在 reload 前进来的请求耗时较长,且`worker_shutdown_timeout `设置较短,这样的话请求就返回报错了。但是这个不影响,还是看实际场景的😂,我是想到了这个例子哈哈。
73 天前
回复了 doggg 创建的主题 程序员 理解 Nginx 的优雅退出机制
问题 1:
从大佬文章介绍出发,先看如何关闭 listen port
发现下列代码
``` c

ls = cycle->listening.elts;
for (i = 0; i < cycle->listening.nelts; i++) {

#if (NGX_QUIC)
if (ls[i].quic) {
continue;
}
#endif

c = ls[i].connection;

if (c) {
if (c->read->active) {
if (ngx_event_flags & NGX_USE_EPOLL_EVENT) {

/*
* it seems that Linux-2.6.x OpenVZ sends events
* for closed shared listening sockets unless
* the events was explicitly deleted
*/

ngx_del_event(c->read, NGX_READ_EVENT, 0);

} else {
ngx_del_event(c->read, NGX_READ_EVENT, NGX_CLOSE_EVENT);
}
}

ngx_free_connection(c);

c->fd = (ngx_socket_t) -1;
}

```
cycle->listening.elts 明显存储着监听相关对象
接着全局查询 cycle->listening.elts

https://github.com/nginx/nginx/blob/6f7494081ae8a56664afb480eff583d639b60ab4/src/core/ngx_cycle.c#L505-L620 部分找到代码,这部分应该是处理 listening 数组的一些信号,调用 ngx_open_listening_sockets 开始监听 cycle 中的端口。

那他真的是零停机吗?

从流程上来看是的,ngx_init_cycle 阶段就已经开始监听端口了,在启动新 worker 后,会按照顺序删除用于接受 IO 通知的事件,关闭监听端口,关闭空闲连接,之后 ngx_process_events_and_timers 会保证处理完所有的未完成的请求。

但是这一切都是基于在指定 worker_shutdown_timeout 时间内能够执行完请求的前提下,能够正常处理完请求,所以如果在这段时间内处理不完,或者接口 duration 超过设置超时时间,那这个请求就会来不及处理,就结束了。
所以,worker_shutdown_timeout 设置要贴合实际场景。这个值如果设置非常大,就会有 worker 进程泄露的风险,设置的比较小,就会 reload 期间,存在接口返回报错。

我认为 nginx 已经处理得很好了,在 reload 期间,有些耗时较长的接口会存在一定问题,但是要根据具体场景,去规划这个超时值就可以避免,我认为他是零停机的。
74 天前
回复了 doggg 创建的主题 程序员 理解 Nginx 的优雅退出机制
@NeedI09in 补充一下,如果说的有误,麻烦大佬斧正,我对 nginx 底层源码了解甚少,只是看了这篇文章,尝试了解了一下源码。如果说的有什么不妥的地方,烦请大佬斧正,感谢。
不知道 ngx_close_idle_connections 是否是只 close 客户端的长连接,烦请大佬斧正。
74 天前
回复了 doggg 创建的主题 程序员 理解 Nginx 的优雅退出机制
问题 2:
从小聪明的角度,感觉函数调用名字是 ngx_close_idle_connections ,应该只是关闭 client 和 upstream 的空闲连接。
从源代码角度分析,收到 NGX_RECONFIGURE_SIGNAL 信号后会走到 ngx_reconfigure ,会通过 ngx_start_worker_processes 启动一个新的 worker ,然后 ngx_signal_worker_processes 会处理掉旧 worker 。旧 worker 的处理方式跟大佬文章里写的一样,旧 worker 的 connection 是否还有效,我是从 ngx_close_idle_connections 出发看他是怎么获取的 connection 。发现他是这么取的 connections
``` c
void
ngx_close_idle_connections(ngx_cycle_t *cycle)
{
ngx_uint_t i;
ngx_connection_t *c;

c = cycle->connections;

for (i = 0; i < cycle->connection_n; i++) {

/* THREAD: lock */

if (c[i].fd != (ngx_socket_t) -1 && c[i].idle) {
c[i].close = 1;
c[i].read->handler(c[i].read);
}
}
}

```


侧面追踪发现 shutdown 超时也会出发关闭连接。大胆猜测 cycle->connections 就是连接池

``` c

static void
ngx_shutdown_timer_handler(ngx_event_t *ev)
{
ngx_uint_t i;
ngx_cycle_t *cycle;
ngx_connection_t *c;

cycle = ev->data;

c = cycle->connections;

for (i = 0; i < cycle->connection_n; i++) {

if (c[i].fd == (ngx_socket_t) -1
|| c[i].read == NULL
|| c[i].read->accept
|| c[i].read->channel
|| c[i].read->resolver)
{
continue;
}

ngx_log_debug1(NGX_LOG_DEBUG_CORE, ev->log, 0,
"*%uA shutdown timeout", c[i].number);

c[i].close = 1;
c[i].error = 1;

c[i].read->handler(c[i].read);
}
}
```

那就追踪 cycle ,从 ngx_master_process_cycle 函数追踪到这一行代码
``` c

cycle = ngx_init_cycle(cycle);
if (cycle == NULL) {
cycle = (ngx_cycle_t *) ngx_cycle;
continue;
}

```

显然,如果这里的 ngx_init_cycle 返回是 NULL ,那么长连接就会无效,问题就回到了 ngx_init_cycle 里发生了什么。大胆猜测这个 init_cycle 正常情况返回自己,异常情况返回 Null 。点进去还真是。
所以结论就是非空闲长连接不会释放,cycle 还是老 cycle ,看起来很合理

不知道我推论对不对,烦请大佬解惑。大佬的文章收益匪浅,看完有种会捕鱼了的快乐,非常感谢。
不过想请教大佬 ngx_temp_pool 是做什么用的,为何 ngx_temp_pool 是 Null 会需要清理长连接呢?
也就是这段代码 https://github.com/nginx/nginx/blob/master/src/core/ngx_cycle.c#L778C1-L801C6

最后感谢大佬的输出,受益匪浅。
怎么方便怎么来吧
75 天前
回复了 gongxuanzhang 创建的主题 程序员 在一个群里被恶心坏了
@NeedI09in 补充一下,开发始终是一个百密不如一疏的行业,有人提出疏忽其实是好事,但是阴阳怪气和鄙夷(这点都不知道)这种态度是不对的,“你知道”并不意味着你可以阴阳怪气和鄙夷。
关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   963 人在线   最高记录 6679   ·     Select Language
创意工作者们的社区
World is powered by solitude
VERSION: 3.9.8.5 · 14ms · UTC 21:01 · PVG 05:01 · LAX 14:01 · JFK 17:01
Developed with CodeLauncher
♥ Do have faith in what you're doing.