Nodejs EventLoop(带有集群模块)和Golang Scheduler的比较

时间:2018-07-17 07:45:18

标签: node.js go goroutine event-loop

在nodejs中,主要批评者基于其单线程事件循环模型。

nodejs的最大缺点是无法在应用程序中执行CPU密集型任务。出于演示目的,让我们以while循环为例(这也许类似于db函数返回十万条记录,然后在nodejs中处理这些记录。)

while(1){
    x++
}

此类代码将阻塞主堆栈,因此在事件队列中等待的所有其他任务将永远无法获得执行的机会。 (在网络应用程序中,新用户将无法连接到该应用程序)。

但是,人们可以使用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返回大量行,然后应用程序需要处理和修改这些行中的某些内容)。不会阻止传入的用户,其他所有任务也会被阻止吗?

那么,如何解决上述问题?

P.S
也许,我提到的用例没有多大意义? :)

1 个答案:

答案 0 :(得分:3)

当前(Go 1.11和更早版本)您所谓的 紧密循环确实会阻塞代码。 发生这种情况仅仅是因为当前Go编译器 插入执行“抢占检查”的代码(« 到调度程序,以便它运行另一个goroutine?»)仅在 它编译的函数的序言(几乎,但让我们别离题)。 如果您的循环未调用任何函数,则不会进行抢占检查 将被制成。

Go开发人员很清楚这一点 和are working on eventually alleviating this issue

不过,请注意,您所指控的问题是在 最真实的场景:执行时间长的代码 运行CPU密集型工作,而无需调用任何函数 很稀有,介于两者之间。

在某些情况下,您确实拥有这样的代码并且您拥有 检测到,这确实使其他goroutine饿了 (请让我强调:您已经通过剖析检测到 而不是仅仅想出“它一定很慢”),您可以 应用几种技术来解决这个问题:

  • 在某些关键点插入对runtime.Gosched()的呼叫 您长时间运行的CPU密集型代码。 这将强制放弃对另一个goroutine的控制 而实际上并没有暂停调用程序goroutine(因此它将 请尽快重新运行。
  • 专用于运行goroutine的OS线程 这些CPU猪:
    1. 将这类CPU猪的集合绑定到N“工人goroutines”中;
    2. 在他们前面放一个调度员(称为"fan-out");
    3. 确保Nruntime.GOMAXPROCS引发后者,以便您有N个额外的线程。
    4. 通过调度程序将工作单元转交给那些专用的goroutine。