odoo多workers下websocket的并发设置

接前面讲过的新建一个websocket server并结合进odoo中,从而给某些特定的页面进行消息提醒的业务(参见https://mixoo.cn/2018/04/16/odoo-websocket/)

没有考虑到的问题

前边的开发和测试都是在单核模式下,并没有考虑到多个worker的情况。对于多个worker并发的时候,我们前面的代码就不能正常的工作了,因为每个独立的进程都用于自己独立的内存,虽然python多线程下有Queue、Array和manager等针对多进程提供的解决方案,但因为我们的模块是嵌入在某个单独的模块中的,并没有在odoo启动的时候开启多个进程(其实这也应该是一种解决思路,但是需要改动odoo的核心代码,无法用继承的方式实现,这样的话就有可能污染了odoo核心模块的代码,并不是一种干净的解决方式)。那么我们究竟应该怎么解决这个问题呢?

首先我们想来分析一下这个问题出现的原因,架设我开了两个worker,对应两个进程A和B,假设A进程首先拿到了tornado的启动权限,并开启了一个线程持续监听9000端口,以对外提供服务。B进程启动后,发现9000端口已经被占用,因为就不再监听9000端口。两个进程相安无事地进行工作去了。

某个时间,一个client发起了连接websocket的请求,如果这个时候恰巧是A进程在处理这个request,那么该client将能够正常的收到消息回应。但如果此刻是B进行在值班,那么B进行去检查当前连接的live connections时就会发现,这个列表是空的(因为真正的连接列表在A进程中),所以B进程不会给client任何回应(实际上也给不了任何回应,连接在A手里)。

这就是为什么在开了多workers后,会有时候能收到消息,而有的时候却没有反应的原因。

解决的方案

周末两天过的混混沌沌,一个问题接着一个问题的出现。思路也没有很清晰。但是的思路一直是想要让A和B进程能够共享同一连接。试过Queue、Array、Manager都无法解决这个问题,后来查到一个解决方案,即用redis作持久化,这样就能让A和B共享同一变量了,但最终也是失败了,失败的原因在于socket连接是不能序列化的,也是不能让A和B进程共享的。

后来想着甚至不行就单独写一个服务算了,让触发事件的时候给服务器发消息,在由服务给各个client发送消息,但是这样做的问题就是部署麻烦了。

有时候,解决问题的方法很简单,甚至花费的时间是思考问题的百分之一,但是思考的这个过程,却百转千回。
这个问题的最终解决方案,我想的是,在触发事件发生的时候,把当前进程当成一个client然后向本机的9000端口发送一个消息(这是里refresh),相当于如果当前值班的进程是B的话,给端口9000发送一个消息,然后A收到这个消息,一看是让它通知所有客户端执行一个动作。其实就是把A当成了B的一个代理,这样就完美解决了多woker的问题。

PS: 如果A进程由于某个原因挂了,会不会影响Websocket服务呢?答案是不会,因为如果A挂了,B就有机会去拿到9000端口的监听权限,为什么是有机会?因为这时候,odoo又起了一个新worker:C。

你的支持我的动力