You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
// 监听端口冲突detectPort((err,port)=>{/* istanbul ignore if */if(err){err.name='ClusterPortConflictError';err.message='[master] try get free port error, '+err.message;this.logger.error(err);process.exit(1);return;}this.options.clusterPort=port;this.forkAgentWorker();// 如果端口没有冲突则执行该方法});
egg-cluster是什么
为了将多核CPU的性能发挥到极致,最大程度地榨干服务器资源,egg采用多进程模型,解决了一个Node.js进程只能运行在一个CPU上的问题,egg-cluster是用于egg多进程管理的基础模块,负责底层的IPC通道的建立以及处理各进程的通信
egg多进程模型
master
的子进程,一般是根据服务器有多少个CPU启动多少个这样的worker
进程,主要用于对外服务,处理各种业务层面的事情master
的子进程,主要处理公共资源的访问,如文件监听,或者帮worker处理一些公共事务,如一些事情是不需要每个worker
都做一次的,agent
帮忙做完之后通知它们执行之后的操作master
类似于一个守护进程的存在:agent
的启动、退出、重启worker
进程的启动、退出、以及refork,在开发模式下负责重启agent
和各个worker
之间的通信worker
之间的通信各进程的启动顺序:
master
启动后先启动agent
进程agent
初始化成功后,通过IPC
通道通知master
master
根据CPU的个数启动相同数目的worker
进程worker
进程初始化成功后,通过IPC
通道通知master
master
通知agent
和各个worker
进程应用启动成功启动方式差异:
从上图可以看出,
master
启动agent
和worker
的方式明显不一样,启动agent
使用的是child_process
的fork模式,启动各个worker
使用的是cluster
的fork模式,为什么不能都使用同一种方式来启动?因为它们所负责处理的事情性质是不一样的,agent
是类似于作为各个worker
秘书的存在,只负责帮它们处理轻量级的服务,是不直接对外提供http访问的,所以master
用cluster.fork
把各个worker
启动起来,并提供对外http访问,这些worker
在cluster
的预处理下能够对同一端口进行监听而不会产生端口冲突,同时使用round-robin策略进行负载均衡把收到的http请求合理地分配给各个worker
进行处理进程间通信:
master
和agent/worker
是real communication,agent
和worker
之间以及各个worker
之间是virtual communicationmaster
继承了events模块,拥有events监听、发送消息的能力,master
进程自身是通过订阅者模式来进行事务处理的,所以在master
的源码里面并没有看到过多的callback hell
master
是agent
的父进程,相互可以通过IPC通道进行通信master
是worker
的父进程,相互可以通过IPC通道进行通信agent
和各个worker
之间毕竟是不同进程,是无法直接进行通信的,所以需要借助master
的力量进行转发,egg-cluster
封装了一个messenger
的工具类,对各个进程间消息转发进行了封装worker
之间由于是不同进程,也是无法直接进行通信的,需要借助master
的力量进行转发,原理同上各进程的状态通知
worker
启动成功后master
会对其状态进行监听,对于退出或者失联的worker
master
是清楚的,在这情况下master
会对这些worker
之前所绑定的事件进行销毁防止内存泄露,并且通知agent
,最后refork出同等数量的worker
保证业务的顺利进行,对worker
的fork和refork操作都是通过工具类cfork
进行的agent
启动成功后master
会对其状态进行监听,对于退出或者失联的agent
master
是清楚的,在这情况下master
会对这些agent
之前所绑定的事件进行销毁防止内存泄露,并且通知各个worker
,最后重启agent
进程保证业务的顺利进行master
退出了或者失联了,worker
怎么办?不用担心,cluster
已经做好了这样的处理,当父进程退出后子进程自动退出master
退出了或者失联了,agent
也像worker
一样退出吗?然而并不是!这是child_process.fork
和cluster.fork
的不同之处,master
退出了或者失联了,agent
进程还继续运行,但是它的父进程已经不在了,它将会被init
进程收养,从而成为孤儿进程,当这样的孤儿进程越来越多的时候服务器就会越来越卡。所以master
退出后需要指定agent
也一起退出!开发模式
开发模式下
agent
会监听相关文件的改动,然后通知master
对worker
进行重启操作Talk is cheap. Show me the code
准备工作
写这篇文章的时候egg社区版最新版是 1.6.0 ,下面的内容以该版本为准
读源码前需要理解两个模块的作用:
master
,agent
,worker
IPC通信的消息转发worker
的启动,状态监听以及refork操作egg是通过
index.js
作为入口文件进行启动的,输入以下代码然后就可以成功启动了入口文件代码如此简单,那egg底层做了些什么?比如
egg.startCluster
这个方法里面做了些什么?原来
egg.startCluster
是egg-cluster
模块暴露的一个APIstartCluster
主要做了这些事情master
进程callback
方法,比如希望在egg启动成功后执行一些业务上的初始化操作Master(egg-cluster/lib/master.js)
Master#constructor
constructor
里面大致可以分为5个部分:Master#forkAgentWorker
master
进程以child_process
模式启动agent
进程到这里,
agent worker
已完成启动,并且master
对其进行监听,这里有个疑问以
child_process.fork
模式启动agent worker,读取的是agent_worker.js
,截取里面的一段代码agent
启动成功后调用process.send()
通知master
,master
监听到该消息通过messenger
转发出去最终由
master
进行agent-start
事件的响应Master#onAgentStart
agent
启动后的操作值得注意的是此时
app worker
还没启动,所以该消息会被丢弃,后续如果发生agent
重启的情况会被app worker
监听到Master#forkAppWorkers
master
进程以cluster
模式启动app worker
进程app worker
启动后,跟agent
一样,通过messenger
发app-start
事件发送给master
,由master
继续处理Master#onAppStart
app worker
启动后的操作这时
agent
和各个app worker
已经ready了,master
也可以做好准备了,执行ready
后的操作,把egg-ready
事件发送给parent
、app
、agent
,告诉它们已经ready
了,可以开始干活Master#onAgentExit
agent
退出后的处理Master#onAppExit
app worker
退出后的处理Master#onReload
开发模式下监听文件的改动,对
app worker
进行重启操作egg-development
插件,对相关文件进行监听,监听到有文件改动的话向master
发送reload-worker
事件master
通过监听reload-worker
事件后执行onReload
方法onReload
通过cluster-reload
模块进行重启操作Master#onExit
master
退出后的处理,该方法主要是打相关的logMaster#onSignal和Master#close
测试的时候,
master
对收到的各个系统signal
进行响应app worker
进程agent
进程master
进程The text was updated successfully, but these errors were encountered: