egg多进程模型注意事项梳理

背景

最近在项目中使用egg进行服务端开发,在开发过程当中遇到了比较诡异的问题,具体表现为mq在监听到信息时,其回调函数会被屡次执行,那么这会致使某个文件被同时操做等问题。vue

问题成因

这边梳理egg文档时,重点过了一下egg多进程的设计模式,了解到egg的master-agent-worker模式,那么这里面有些问题是须要咱们在开发时去注意的了。node

首先介绍下egg的多进程实现方式

egg经过node提供的cluster实现了多进程模式,为了更好地利用多核环境,egg通常会启用至关于cpu核数的worker,以此来最大化利用cpu能力。设计模式

在egg启动时,master,agent,worker的关系如图所示app

+---------+           +---------+          +---------+
|  Master |           |  Agent  |          |  Worker |
+---------+           +----+----+          +----+----+
     |      fork agent     |                    |
     +-------------------->|                    |
     |      agent ready    |                    |
     |<--------------------+                    |
     |                     |     fork worker    |
     +----------------------------------------->|
     |     worker ready    |                    |
     |<-----------------------------------------+
     |      Egg ready      |                    |
     +-------------------->|                    |
     |      Egg ready      |                    |
     +----------------------------------------->|

在这种模式下,master、agent、worker各司其职,主要制做分配以下:
master:负责维护整个应用稳定性,当有worker因异常而退出时,master负责拉起新的worker,以确保应用正常运行。
agent:因为egg的多进程模型会在每一个进程中运行一份咱们的应用实例,那么在某些状况下,这种机制会致使问题。好比,保存日志的逻辑若是在每一个进程中都执行的话,那么在触发日志保存操做的时候,会有多个进程同时操做日志文件,那么此时就会致使文件读写问题。因此egg设计了agent进程,agent进程只会有一个,不会出现上述问题,这样,对于相似上述的后台运行的逻辑就统一放到agent中去处理了。
worker:负责执行业务代码,处理用户请求和定时任务,egg在框架层保证了定时任务只会在单个worker中执行,因此能够放心使用。框架

分析egg多进程致使的问题

上面咱们分析过了egg的多进程机制,因此咱们知道了问题成因,出现咱们最开始说的问题的缘由是咱们把mq的监听和处理逻辑放到了worker中,那么这样的话在实际运行过程当中,就会致使mq收到消息时,回调函数被执行屡次。dom

到这里咱们已经知道如何优化了,那就是把mq的处理逻辑放到agent中,以确保mq消息的回调仅执行一次。可是细心地你确定发现了,这里有个问题,agent只有一个实例,若是事情在agent里面作,那么不是没法利用多核性能了吗?

agent与worker通讯

的确,咱们能够在agent中处理仅须要单次执行的逻辑,可是这样作就无法利用多核性能了。那么有什么办法吗?没错,就是进程间通讯,具体思路就是,agent仍是负责mq的链接和监听逻辑,可是回调函数不在agent中执行,而是写在worker里面。那么worker何时执行这个逻辑呢?答案是,agent经过进程间通讯通知worker。egg内部实现了一个进程间通讯机制,咱们直接调用便可,主要实现方式以下:async

广播消息: agent => all workers
                  +--------+          +-------+
                  | Master |<---------| Agent |
                  +--------+          +-------+
                 /    |     \
                /     |      \
               /      |       \
              /       |        \
             v        v         v
  +----------+   +----------+   +----------+
  | Worker 1 |   | Worker 2 |   | Worker 3 |
  +----------+   +----------+   +----------+

指定接收方: one worker => another worker
                  +--------+          +-------+
                  | Master |----------| Agent |
                  +--------+          +-------+
                 ^    |
     send to    /     |
    worker 2   /      |
              /       |
             /        v
  +----------+   +----------+   +----------+
  | Worker 1 |   | Worker 2 |   | Worker 3 |
  +----------+   +----------+   +----------+

这里咱们能够看出来,进程间通讯都是基于master转发的,因此咱们能够利用egg提供的机制,解决咱们的问题。函数

解决办法

如上文分析,咱们把mq的链接和监听逻辑放到agent中,当接收到消息时,经过进程间通讯把通知发送给worker,而后由worker执行具体的业务逻辑便可。具体代码其实能够参考vue的事件机制,在worker中监听指定事件:性能

app.messenger.on(action, data => {
  // 执行业务逻辑
});

在agent中创建mq链接并监听消息,收到消息后触发事件:优化

exports.task = async ctx => {
  ...// 收到mq消息的逻辑此处省略
  // 准备发送通知
  ctx.app.messenger.sendRandom(action);
};

注意,须要单次执行的任务要调用sendRandom方法,这个是发送给一个worker的方法。固然,若是要执行屡次的,能够调用app.messenger.sendToApp()方法,这个方法会把消息发送给全部worker,并执行屡次处理逻辑。

总结

egg在多进程模型中的使用仍是须要有一些技巧的,因此须要咱们先熟悉egg的多进程机制后再进行业务开发,避免遇到奇怪的坑,浪费时间。

相关文章
相关标签/搜索