local num = 1
while true do
    local sock = ngx.socket.tcp()
    local ok, err = sock:connect("127.0.0.1", 6379)
    num = num + 1
    if num % 10 == 0 then
            ngx.sleep(0.001)
    end
end

上面的代码中6379 不存在监听, 在 timer 或 连接处理, stream 或http 模块均存在 内存泄露.
如果一个 全局定时器在 长时间运行中, 可能会 尝试建立多次连接(由于后端异常可能会有多次尝试).
这在长请求(如定时器启动的全局协程)有负面影响:
例如, redis 集群某个节点一直连接拒绝, 但是 我们不能直接认为全部redis后端异常, 大量事务处理导致大量的连接报错, 最终修复redis后, openresty 由于该内存问题, 可能需要重启. 这个例子假设了redis操作在一个请求(定时器)中.

频繁连接错误下, 连接池也无法避免.
socket应避免在一个请求中频繁断连.频繁错误的host需要有不可用 周期.

标题应改为 超长时间的长请求(或定时器全局协程, stream 长连接)中, 频繁sock:connect错误存在内存泄露风险.

    14 days later

    和这篇文章 描述一致. 原因是 connect时 ngx_palloc host_len, 小块内存, close时也没有被释放, 而且即使使用ngx_pfree, 它也不会释放小块内存. connect次数过多导致内存上涨过快. 使用jemalloc/mimalloc管理内存是否更好?

    既然每个 timer 都是一个请求,那么如果你每个网络请求都会启动一个甚至多个 timer,性能自然好不到哪儿去。最简单的优化办法是引入批处理,避免不断创建 timer,你也可以考虑下队列,甚至更为复杂的时间轮。不过要想复用 timer,还要面对额外的挑战……

    第一个挑战来自于 Nginx 的每请求内存池。只有在请求结束时,Nginx 才会释放这一内存池内所有的内存。而前面已经说了,每个 timer 在 Nginx 看来都是一个请求,所以某种意义上,一个 timer 就像是一个长连接,尤其当这个 timer 会一直运行到进程结束时。长时间运行的 timer 自然会带来内存的持续上涨,但其上涨的速度一般而言并不显著。原因有二:

    许多 OpenResty API 实际上只会分配 Lua 层面上的内存。而这部分内存是在 LuaJIT GC 管理下的。在引入了 lua-resty-core 后更是如此。
    cosocket 的 send/receive 操作,会有 buffer 复用机制,使得其内存占用不会无限制增加。
    另一个挑战来自于较为隐晦的地方。当前 entry thread 会把它所创建的每个协程,记录到一个链表里。而各种协程 API,大都需要访问这个链表。如果 timer 或者长连接持续大量地创建协程,会导致协程 API 变得越来越慢。就目前的情况,要想解决这个问题,需要对协程进行复用,避免无限制地创建协程。

      Write a Reply...