python 连接rabbitmq出现的诡异进程盗取消息

root6个月前python37

本文在py2下执行,

由来:
    因业务需要,python创建子线程后再次创建子进程(用于执行shell命令)。没错就是在子线程里面创建子进程。都知道py2的坑还是蛮多的。

问题出现:在某次运行中出现了一个情况,mq的管理界面有一个消息一直是unack状态。通过查看发送端的信息,在接收端中的数据库中并没有找到该数据。

在mq管理面板发现了两个消费者,消费的ip地址还一样,只是端口不一样。通过这里的两个不同的端口去查看服务器到底是什么程序占用这个端口。
发现一个是正常的主进程,一个是子进程x。

    顿时头皮发麻,子进程没有丝毫接收mq消息的代码,而且都是比较简单的shell命令。
但是发现的x子进程的进程号都比主进程号小,极其的不正常。
    然后kill掉正常的主进程,mq管理面板的消费者少一个,讲道理现在一个消费者都不应该有。通过剩余的消费者的端口号查看服务器的服务

发现是刚才的x子进程。为什么这个x子进程没有挂?这是个问题。
    我再次重启程序,主进程启动,mq的消费者增加一个。服务器的服务还是主进程和之前的x子进程占用端口消费消息。
好在这个有问题的消费者只消费了一个且一直是unack。    


    再次kill掉主程序,然后剩下x子进程。这很可能是之前某次主进程启动触发的,关闭主进程后并没有随着关闭。

    当关闭主进程之后,在关闭这个x子进程,处于unack的消息被释放重新进入ready状态,可以被消费。


主问题:关闭主进程,出现子进程没有关闭,且消费了一个mq消息。,bug不可主动重现,且重现率极低。


问题1:为什么子进程没有关闭?以后还出重现吗?
解决思路就是关闭主线程的时候干掉子线程。


问题2:为什么子线程会接收mq消息,子线程的业务关系跟mq没有任务关联。


存在的关联关系是:rabbitmq的消费出现一个unack的,必然有一个僵尸子进程存在

存在的现象:当两次启停主程序时,会出现两个消费者和第一次主程序的僵尸进程。但是过一会僵尸进程和一个消费者就会消失。

疑惑之处:启动一次主程序会在运行中多次启动子进程,代码中并没有强制随主进程关闭,但是自己执行完会关闭。因为关闭主进程时,子进程的数远小于创建的子进程数。且过数秒钟所有子进程就全部关闭。


    经过和同事的分析和查看linux的进程运行信息。
初步猜想:子进程存在某些原因没有关闭(先不管什么原因),子进程会继承主进程的资源,包含消费的端口资源等,这个子进程成为僵尸进程,占用端口,保持连接,但是无法处理消息。


首要处理方案:

    关闭主进程时,逐一把所有没有关闭的子进程关闭。避免僵尸进程的出现。


执行方案:

    在主进程中开启的子进程设置为独立的,不在继承主进程的资源。

执行效果:关闭主程序是,mq消费者会立即消失。子进程缓慢关闭。


当然,还是在接下来的开发中,加上对子进程的回收。
哎掉头发的一天,还要感谢公司大佬同事的鼎力支持!方才包住自己的狗头发

    




    

相关文章

自定义logger 模块使调用打印日志的文件为调用文件而不是logger模块

更新logging 源码1248行f f f.f_back替换为f f f.f_back     f_2 f...

selenium控制webdriver  设置请求头。只能设置简单的。自定义和固定的格式无法修改成功

selenium控制webdriver 设置请求头。只能设置简单的。自定义和固定的格式无法修改成功

time selenium webdriver options webdriver.() options.() options.( options.() bro...

父进程退出后如何退出子进程

我们知道当子进程推出的时候,父进程会收到 SIGCHLD 信号,从而可以采取相应的操作。但是当父进程退出的时候,系统会把子进程的父进程更改为pid=0的 init 进程,而且子进程不会收到任何信号。而...

python 的configparser 读取配置文件遇到%特殊符号

test.ini 配置文件中有mysql的密码,且密码含有“%”这个特殊符号因为%在py是转义符的含义需要对该字符转义即修改  %  为 %%用%对%进行转义...

python3.5.2版本不支持的语法格式

在使用geoip2的时候,运行被告知包中语法错误查看详情发现在python 3.5中不支持注释var类型如下语法 _buffer: Union[bytes, FileBu...

python os 模块文件常用操作

123456import os #回去当前文件路径os.path.realpath(__file__)#获取文件是否存在os.path.exists(filepath)#获取文件大...