py 多进程 引发的 各种数据库连接 消息队列连接 异常问题 简单分析

fork 引起的一些问题

multiprocess 建立在 fork 基础上, 所以fork 会引发很多问题,比如 多个进程使用相同的 fd。
如果只是 fd 的话可能冲突概率还不大。 但是 数据库 连接 , 尤其 消息队列的 心跳 连接,肯定会出现问题,

两种解决方法

先 释放 conn ,再 创建

  1. 在 fork 或者 multiprocess 之前 先去,dispose 释放 所有连接。
  2. 再在 数据库 或者 消息队列中实现, 对应的 retry 方法 当 执行前 自行创建 新的连接。

先 创建 proc ,再释放

不推荐

解决思路

当前很多 数据库连接中 以及消息队列连接 中 都有 自带的 dispose 方法
去在 进程拷贝之前 dispose 掉 现有的 连接对象。
dispose 和 close 的差别,
close 以后 ,还可以open 得到 同属性的连接,
但是 dispose 之后 ,会将 对象 彻底释放,下次使用必须 重新 create 连接对象。如此一来 就不会产生进程间的 连接冲突问题。

一些实际工程中的 demo

py3 oslo_service fork 启动子进程

python3.7/site-packages/oslo_service/service.py

_start_child 之前 invoke 了 _child_process

  1. 关闭了写管道
  2. 重置了随机种子
  3. 翻译不好 直接把 代码留下吧
  def _child_process_handle_signal(self):
     # Setup child signal handlers differently
     。。。。

完整代码

class ProcessLauncher(object):
    ......省略
    def _child_process(self, service):
        self._child_process_handle_signal()

        # Reopen the eventlet hub to make sure we don't share an epoll
        # fd with parent and/or siblings, which would be bad
        eventlet.hubs.use_hub()

        # Close write to ensure only parent has it open
        os.close(self.writepipe)
        # Create greenthread to watch for parent to close pipe
        eventlet.spawn_n(self._pipe_watcher)

        # Reseed random number generator
        random.seed()

        launcher = Launcher(self.conf, restart_method=self.restart_method)
        launcher.launch_service(service)
        return launcher

    def _start_child(self, wrap):
        if len(wrap.forktimes) > wrap.workers:
            # Limit ourselves to one process a second (over the period of
            # number of workers * 1 second). This will allow workers to
            # start up quickly but ensure we don't fork off children that
            # die instantly too quickly.
            if time.time() - wrap.forktimes[0] < wrap.workers:
                LOG.info('Forking too fast, sleeping')
                time.sleep(1)

            wrap.forktimes.pop(0)

        wrap.forktimes.append(time.time())

        pid = os.fork()
        if pid == 0:
            self.launcher = self._child_process(wrap.service)
            while True:
                self._child_process_handle_signal()
                status, signo = self._child_wait_for_exit_or_signal(
                    self.launcher)
                if not _is_sighup_and_daemon(signo):
                    self.launcher.wait()
                    break
                self.launcher.restart()

            os._exit(status)

        LOG.debug('Started child %d', pid)

        wrap.children.add(pid)
        self.children[pid] = wrap

        return pid
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

yuemake999

请我喝茶呗

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值