事件循环:JavaScript、Node.js、PHP-Swoole、PHP-workerman

在浏览器端,JavaScript的主要是与用户互动,以及操作DOM。为了避免复杂性,JavaScript被设计为只能运行在单进程单线程中。H5提出了Web Worker标准,允许JavaScript创建多个线程,但子线程受主线程控制,且不得操作DOM。

在JavaScript中,任务分为同步任务和异步任务(异步任务不堵塞当前线程),所有同步任务都在主线程上执行,形成一个执行栈,另外存在一个“任务队列”,如果异步任务有了结果,就在“任务队列”中添加一个事件(对应回调),一旦“执行栈”中的所有同步任务执行完毕,就会读取“任务队列”,取出事件对应的回调,然后进入执行栈执行回调(注:任务队列中对应的是事件,事件可以对应多个回调,回调的执行也依赖事件对象记录的原始参数)。

事件的产生包括用户比如用户点击,异步调用后触发的事件,取出事件是一个循环过程(执行栈空),所以这个过程叫事件循环(event loop)。

Node.js中的事件循环原理上和JavaScript中的(应该是浏览器)并没有很大不同。Node.js中为了对付大量的链接,使用了epoll,对于异步任务,使用了一个线程池来模拟,回调的执行全部落在主线程中,所以它是单进程单线程的(这个也是为何面对CPU密集运算时的场景不合适,尽管还有其它的CPU是空闲的,因为执行回调的,仅一个线程)。

PHP中的一个扩展Swoole,理念上和Node.js是差不多的,但是架构上有很大不同。Swoole启动后,首先启动一个主进程,在这个进程内其它若干个React线程,这些线程专门负责监听,接收数据和响应,然后启动一个manager进程和一组Worker进程和若干task进程,manager进程主要用来监控worker进程和task进程(比如退出重启等),Worker可以把耗时的任务投递给task进程,task执行时同步堵塞的,执行完毕后通过进程间通信的方式通知Worker进程。每个Worker进程维护一个事件循环,并在Worker进程内执行回调(可以应用到多核CPU)。

PHP-workerman相对Swoole来说,架构上就比较简单。它相当于只有Swoole的Worker进程这部分。每个Worker都相互独立的监听端口,执行回调,响应数据等。