当前位置: 首页 > 后端技术 > Node.js

Egg多进程模型注意事项

时间:2023-04-03 15:54:18 Node.js

背景最近在项目中使用egg做服务端开发。在开发过程中,遇到了奇怪的问题。具体表现为MQ监听信息时,其回调函数会被多次调用。执行,那么这样就会出现同时操作一个文件等问题。问题原因这里在整理egg文档的时候,重点关注了egg的多进程设计模式,了解了egg的master-agent-worker模式,所以里面有一些我们需要的问题开发时要注意。首先介绍一下egg的多进程实现方法。Egg通过node提供的集群来实现多进程模式。为了更好的利用多核环境,eggs一般会启用相当于CPU核数的worker,最大限度的利用CPU的能力。egg启动时,master、agent、worker的关系如图+---------++---------++---------+|大师||代理||工人|+--------++----+----++----+----+|分叉代理||+--------------------->|||代理准备好|||<--------------------+|||叉工|+-------------------------------------------->||工人准备好|||<----------------------------------------+|鸡蛋准备好||+-------------------->|||鸡蛋准备好||+---------------------------------------->|在这种模式下,主人、agent、worker各司其职,主要生产任务如下:master:负责维护整个应用的稳定性,当一个worker异常退出时,master负责拉起一个新的worker以保证应用程序的正常运行。agent:由于egg的多进程模型会在每个进程中运行一份我们的应用实例,所以在某些情况下,这种机制会产生问题。例如,如果保存日志的逻辑是在每个进程中执行的,那么当触发保存日志操作时,多个进程会同时操作日志文件,此时会导致文件读写出现问题。因此egg设计了一个agent进程。只会有一个agent进程,不会出现上面的问题。这样类似上面的后台运行的逻辑就统一放在agent中处理了。Worker:负责执行业务代码,处理用户请求和定时任务,egg在框架层保证了定时任务只会在单个worker中执行,所以可以放心使用。egg多进程导致的问题分析上面我们已经分析了egg的多进程机制,那么问题的原因就知道了。之所以会出现我们一开始提到的这个问题,是因为我们把MQ的监控和处理逻辑放在了worker里面,所以这个在实际运行过程中,会导致mq收到消息时回调函数被执行多次。至此我们已经知道如何优化,即将mq的处理逻辑放在agent中,保证mq消息的回调只执行一次。但是细心的话,一定已经发现这里有问题了。只有一个代理实例。如果事情都在agent里面做,那岂不是无法发挥多核的性能优势?Agent与worker通信我们确实可以在agent中处理只需要执行一次的逻辑,但是这样做,我们无法利用多核性能。那么有什么办法吗?没错,就是进程间通信。具体思路是agent仍然负责MQ的连接和监听逻辑,但是回调函数不在agent中执行,而是写在worker中。那么worker什么时候执行这个逻辑呢?答案是agent通过进程间通信通知worker。egg内部实现了进程间通信机制,我们可以直接调用。主要实现方式如下:广播消息:agent=>allworkers+------++------+|大师|<----------|代理|+--------++--------+/|\/|\/|\/|\vvv+---------++---------++---------+|工人1||工人2||工人3|+----------++----------++----------+指定收件人:一名工人=>另一名工人+--------++--------+|大师|------------|代理|+------++------+^|发送至/|工人2/|/|/v+---------++---------++---------+|工人1||工人2||工人3|+----------++----------++----------+这里可以看出进程通信是基于master转发的,所以我们可以使用egg提供的机制解决了我们的问题。解决方法如上分析。我们把mq的连接和监控逻辑放到了agent里面。当收到消息时,通过进程间通信将通知发送给worker,然后由worker执行具体的Just业务逻辑。具体代码其实可以参考vue的事件机制,在worker中监听指定的事件:app.messenger.on(action,data=>{//执行业务逻辑});在agent中建立mq连接并监听消息,接收消息事件触发后:exports.task=asyncctx=>{...//接收mq消息的逻辑这里省略//准备发送通知ctx.app.messenger.sendRandom(action);};注意,单个请求需要执行的任务需要调用sendRandom方法,也就是发送给一个worker的方法。当然,如果想多次执行,可以调用app.messenger.sendToApp()方法,该方法会将消息发送给所有worker,并多次执行处理逻辑。综上所述,egg在多进程模型中的使用还是需要一些技巧的,所以我们在进行业务开发之前需要先熟悉egg的多进程机制,以免遇到奇怪的坑,浪费时间。