Nodejs EventLoop(带集群模块)和Golang Scheduler的比较
Comparison of Nodejs EventLoop (with cluster module) and Golang Scheduler
在 nodejs 中,主要批评者基于其 单线程事件循环模型。
nodejs 最大的缺点是无法在应用程序中执行 CPU 密集型任务。出于演示目的,让我们以 while 循环为例(这可能类似于返回十万条记录的 db 函数,然后在 nodejs 中处理这些记录。)
while(1){
x++
}
此类代码将阻塞主堆栈,因此事件队列中等待的所有其他任务将永远没有机会执行。 (而在 web 应用程序中,新用户将无法连接到该应用程序)。
然而,可以使用像 cluster
这样的模块来利用多核系统并部分解决上述问题。 Cluster 模块允许创建一个由独立进程组成的小型网络,这些进程可以共享服务器端口,这使 Node.js 应用程序可以访问服务器的全部功能。 (但是,使用 Cluster 的最大缺点之一是无法在应用程序代码中维护状态)。
但是,如果服务器负载过大,我们很有可能再次陷入同样的情况(如上所述)。
当我开始学习Go语言并了解它的架构和goroutines时,我认为它可能会解决由于nodejs的单线程事件循环模型而出现的问题。并且它可能会避免上述 CPU 密集任务的场景,直到我遇到这段有趣的代码,它阻止了所有 GO 应用程序并且什么也没有发生,就像 nodejs 中的 while 循环一样。
func main() {
var x int
threads := runtime.GOMAXPROCS(0)
for i := 0; i < threads; i++ {
go func() {
for { x++ }
}()
}
time.Sleep(time.Second)
fmt.Println("x =", x)
}
//or perhaps even if we use some number that is just greater than the threads.
所以,问题是,如果我有一个负载密集型应用程序,并且还会有很多 CPU 密集型任务,我可能会陷入上述情况。 (其中 db returns 大量行,然后应用程序需要处理和修改这些行中的某些内容)。传入的用户不会被阻止,所有其他任务也会被阻止吗?
那么,如何解决上述问题呢?
P.S
或许,我提到的用例没有多大意义? :)
目前(Go 1.11及更早版本)你所谓的
紧密循环确实会阻塞代码。
发生这种情况仅仅是因为当前 Go 编译器
插入执行 "preemption checks" 的代码(«我应该放弃
到调度器,所以它 运行 是另一个 goroutine?») 仅在
它编译的函数的序言(几乎,但我们不要离题)。
如果您的循环不调用任何函数,则不会进行抢占检查
将制作。
Go 开发者很清楚这一点
和 are working on eventually alleviating this issue.
不过请注意,您所谓的问题在
最真实的场景:执行很长时间的代码
运行 秒的 CPU 密集工作 没有调用任何函数
很少见,而且介于两者之间。
在某些情况下,您确实有这样的代码并且
detected 真的让其他goroutine饿死
(让我强调一下:你已经通过分析发现了这一点——作为
而不是只是想出 "it must be slow"),你可能
应用几种技术来处理这个问题:
- 在某些关键点插入对
runtime.Gosched()
的调用
您的 long-运行ning CPU 密集型代码。
这将强制放弃对另一个 goroutine 的控制
而实际上并没有挂起调用者 goroutine(所以它会
运行 将再次安排)。
- 为 goroutines 提供 OS 个线程 运行ning
那些 CPU 猪:
- 将一组这样的 CPU 猪绑定到,比如说,
N
"worker goroutines";
- 在他们前面放一个调度员(这叫"fan-out");
- 确保
N
明显小于 runtime.GOMAXPROCS
或提高后者,这样你就有了那些N
额外的线程。
- 通过调度程序将工作单元铲到那些专用的 goroutines。
在 nodejs 中,主要批评者基于其 单线程事件循环模型。
nodejs 最大的缺点是无法在应用程序中执行 CPU 密集型任务。出于演示目的,让我们以 while 循环为例(这可能类似于返回十万条记录的 db 函数,然后在 nodejs 中处理这些记录。)
while(1){
x++
}
此类代码将阻塞主堆栈,因此事件队列中等待的所有其他任务将永远没有机会执行。 (而在 web 应用程序中,新用户将无法连接到该应用程序)。
然而,可以使用像 cluster
这样的模块来利用多核系统并部分解决上述问题。 Cluster 模块允许创建一个由独立进程组成的小型网络,这些进程可以共享服务器端口,这使 Node.js 应用程序可以访问服务器的全部功能。 (但是,使用 Cluster 的最大缺点之一是无法在应用程序代码中维护状态)。
但是,如果服务器负载过大,我们很有可能再次陷入同样的情况(如上所述)。
当我开始学习Go语言并了解它的架构和goroutines时,我认为它可能会解决由于nodejs的单线程事件循环模型而出现的问题。并且它可能会避免上述 CPU 密集任务的场景,直到我遇到这段有趣的代码,它阻止了所有 GO 应用程序并且什么也没有发生,就像 nodejs 中的 while 循环一样。
func main() {
var x int
threads := runtime.GOMAXPROCS(0)
for i := 0; i < threads; i++ {
go func() {
for { x++ }
}()
}
time.Sleep(time.Second)
fmt.Println("x =", x)
}
//or perhaps even if we use some number that is just greater than the threads.
所以,问题是,如果我有一个负载密集型应用程序,并且还会有很多 CPU 密集型任务,我可能会陷入上述情况。 (其中 db returns 大量行,然后应用程序需要处理和修改这些行中的某些内容)。传入的用户不会被阻止,所有其他任务也会被阻止吗?
那么,如何解决上述问题呢?
P.S
或许,我提到的用例没有多大意义? :)
目前(Go 1.11及更早版本)你所谓的 紧密循环确实会阻塞代码。 发生这种情况仅仅是因为当前 Go 编译器 插入执行 "preemption checks" 的代码(«我应该放弃 到调度器,所以它 运行 是另一个 goroutine?») 仅在 它编译的函数的序言(几乎,但我们不要离题)。 如果您的循环不调用任何函数,则不会进行抢占检查 将制作。
Go 开发者很清楚这一点 和 are working on eventually alleviating this issue.
不过请注意,您所谓的问题在 最真实的场景:执行很长时间的代码 运行 秒的 CPU 密集工作 没有调用任何函数 很少见,而且介于两者之间。
在某些情况下,您确实有这样的代码并且 detected 真的让其他goroutine饿死 (让我强调一下:你已经通过分析发现了这一点——作为 而不是只是想出 "it must be slow"),你可能 应用几种技术来处理这个问题:
- 在某些关键点插入对
runtime.Gosched()
的调用 您的 long-运行ning CPU 密集型代码。 这将强制放弃对另一个 goroutine 的控制 而实际上并没有挂起调用者 goroutine(所以它会 运行 将再次安排)。 - 为 goroutines 提供 OS 个线程 运行ning
那些 CPU 猪:
- 将一组这样的 CPU 猪绑定到,比如说,
N
"worker goroutines"; - 在他们前面放一个调度员(这叫"fan-out");
- 确保
N
明显小于runtime.GOMAXPROCS
或提高后者,这样你就有了那些N
额外的线程。 - 通过调度程序将工作单元铲到那些专用的 goroutines。
- 将一组这样的 CPU 猪绑定到,比如说,