在ngx.timer.at中使用ngx.thread.spawn和ngx.thread.wait的问题

460 views
Skip to first unread message

shu chen

unread,
Oct 8, 2013, 11:21:03 AM10/8/13
to open...@googlegroups.com
Hi
    我想在ngx.timer.at中使用ngx.thread.spawn产生一个"light thread"不停的从redis中获取消息,每获取一个消息就再产生一个"light thread"去处理消息,然后再把处理的结果push回redis中。
    希望从redis中获取消息的"receive thread"和处理消息的"work thread"互不影响,也就是在"receive thread" spawn "work thread"后,不需要wait(ngx.thread.wait) "work thread"的结果,立刻再去获取消息。但是只允许在parent thread中wait child thread。也就是不能在timer本身的thread中去wait。可是如果在"receive thread"去wait结果,那么一个消息处理很耗时,是不是就影响了"receive thread"去接收新的消息。有什么办法可以处理这种场景吗?

 Thanks

Yichun Zhang (agentzh)

unread,
Oct 8, 2013, 2:42:49 PM10/8/13
to openresty
Hello!

2013/10/8 shu chen:
> 我想在ngx.timer.at中使用ngx.thread.spawn产生一个"light
> thread"不停的从redis中获取消息,每获取一个消息就再产生一个"light thread"去处理消息,然后再把处理的结果push回redis中。

我建议你不要使用这种工作模型。因为你从 redis 拿任务的 light thread 的运行速度可能会超过或者低于你实际处理任务的
light thread,你几乎总是需要同步这两个轻线程之间的速度,否则很容易出现任务大量积压或者任务不足导致的处理消息的线程的饥饿;类似地,push
结果的 light thread 也存在速度不一致时的同步问题。

推荐的做法是创建有限个完全相同的 light thread 作为并行的 worker 线程,每个线程都顺序地(即同步地)从 redis
取任务,再处理消息,再推送结果。这样可以避免轻线程同步的麻烦,同时也可以提高轻线程的利用率(毕竟在线程 A 在等待线程 B 的时候线程 A
其实是在浪费资源)。

当然,你仍然需要考虑你的 timer 处理程序本身的轻线程和你的这些同质的 worker 轻线程之间的同步问题。如果你的 timer
触发的频率高于你实际干活的那些轻线程,则你的那些实际干活的轻线程也会出现大量积压的问题。

在优化 C10K 应用的时候,最重要的一条原则是减少系统资源的浪费,而不是一味地追求异步处理。其实异步处理经常会引入更多的开销和复杂度。

Best regards,
-agentzh

shu chen

unread,
Oct 8, 2013, 11:28:44 PM10/8/13
to open...@googlegroups.com
感谢您的解答

选择这个方式的一个原因是不希望到redis的连接太多,担心影响了redis的性能。因为任务的种类非常多,每一类任务对应了redis中的一个队列。
对于同一类任务,timer只会被触发一次,然后在timer中启动一个或几个接收线程去接收任务,收到一个任务就会spawn一个work线程。
也就是说请求(任务)不是通过http方式到nginx的,而是在timer中的接收线程主动去redis中获取的,触发timer的请求相当于一个管理命令。

在我这种场景下,收任务一般会快于worker线程的处理。所以这么做的原因其实就是看中work线程可以非常多,而且是异步的。不会受限于其他语言中的进程数或者线程数。
当然work线程还是需要控制一下个数,让超出处理能力之外的任务堆积在redis中。
最终效果还要看一下获取任务的速度以及work的处理速度,还有发送响应的速度这几方面的匹配程度。

Thanks

Yichun Zhang (agentzh)

unread,
Oct 9, 2013, 1:31:10 AM10/9/13
to openresty
Hello!

2013/10/8 shu chen:
> 选择这个方式的一个原因是不希望到redis的连接太多,担心影响了redis的性能。因为任务的种类非常多,每一类任务对应了redis中的一个队列。
> 对于同一类任务,timer只会被触发一次,然后在timer中启动一个或几个接收线程去接收任务,收到一个任务就会spawn一个work线程。
> 也就是说请求(任务)不是通过http方式到nginx的,而是在timer中的接收线程主动去redis中获取的,触发timer的请求相当于一个管理命令。
>

轻量级线程也是有开销的,无论是内存方面还是 CPU 方面。虽然相比 OS
线程要“轻”很多。一般地,为了系统整体的吞吐量,我们会努力控制轻线程的个数,防止数量过多。

一般地,通过大量分配异步“轻线程”是提高并发度的做法,而并发度的提高一般会伴随着单位任务的处理延时的增加和系统整体吞吐能力的下降。毕竟系统可用的
CPU 资源是一个常数。类似地,最多可以使用的内存也是一个常数。这是我对你的做法的担心。你可以自己多做一些实际测量。

对于你最初关于 ngx.thread.wait 的问题,我觉得引入一个 no hang 模式的 ngx.thread.wait()
还是有意义的。即在这种模式下,父线程只收割已经结束了的“僵尸子线程”。类似 C 函数 waitpid 的 WNOHANG
选项。或许引入一个新的 ngx.thread.reap() 函数?这样你可以在你的父线程中定期调用 ngx.thread.reap()
回收已经完成了的僵尸子线程。这里最主要的问题是僵尸子线程的积压与 ngx.thread.wait() 的挂起。

Regards,
-agentzh
Reply all
Reply to author
Forward
0 new messages