【问题标题】:Python: How to wait until a subprocess finishes initializingPython:如何等到子进程完成初始化
【发布时间】:2017-07-20 13:30:51
【问题描述】:

我正在尝试为单元测试创​​建一个 Redis 临时实例:

class RedisTemporaryInstance:
    def __enter__(self):
        self.process = subprocess.Popen(['redis-server', '--port', '6399'],
                                        stdout=open(os.devnull, 'wb'),
                                        stderr=subprocess.STDOUT)
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        subprocess.call(['redis-cli', '-p', '6399', 'shutdown'])

但是当我这样使用它时:

import redis
with RedisTemporaryInstance() as temp:
    red = redis.Redis(port=6399)
    print(red.ping())

我来了

ConnectionError: Error 111 connecting to localhost:6399. Connection refused.

但是当我添加一个小睡眠时

with RedisTemporaryInstance() as temp:
    sleep(0.01)
    red = redis.Redis(port=6399)
    print(red.ping())

一切正常。所以显然服务器还没有准备好接受没有睡眠的连接。

我的问题是:有没有更好的方法来等待服务器准备好而不是添加任意睡眠持续时间?

【问题讨论】:

  • Wait for stdout on Popen的可能重复
  • 看看刚刚提出的副本。如果您使用 retry 包,您可以制作一个调用,该调用将继续尝试连接到 redis 服务器,以至少检查它是否“启动”,一旦启动,您就可以继续进行测试。

标签: python sockets server redis subprocess


【解决方案1】:

您应该等待服务器初始化作为 enter 方法的一部分。 您可以使用辅助方法检查打开的端口:

def check_socket(host, port):
    import socket
    from contextlib import closing
    with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as sock:
        if sock.connect_ex((host, port)) == 0:
            return True
        else: 
            return False

class RedisTemporaryInstance:
    def __enter__(self):
        self.process = subprocess.Popen(['redis-server', '--port', '6399'],
                                        stdout=open(os.devnull, 'wb'),
                                        stderr=subprocess.STDOUT)
        retries = 5
        while not check_socket('127.0.0.1', 6399) and retries > 0:
            sleep(0.1)
            retries -= 1
        if retries == 0:
            print 'failure'
        return self

这样你只有在初始化之后才能得到 Redis 对象

【讨论】:

    【解决方案2】:

    你使用 Popen 来启动 redis 服务器。这需要时间(在您的情况下为 0.01 秒),因为打开进程和侦听连接到您的界面。 Popen 是非阻塞的,因此该命令将在您的系统上设置,然后代码继续运行。这是 with 语句在 redis-server 启动之前完成的。我认为你必须在启动redis服务器后在阻塞调用中使用redis-cli ping(使用subprocess.call):

    class RedisTemporaryInstance:
        def __enter__(self):
            self.process = subprocess.Popen(['redis-server', '--port', '6399'],
                                        stdout=open(os.devnull, 'wb'),
                                        stderr=subprocess.STDOUT)
            subprocess.call(['redis-cli', '-p', '6399', 'ping'])
            return self
    
        def __exit__(self, exc_type, exc_value, traceback):
            subprocess.call(['redis-cli', '-p', '6399', 'shutdown'])
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-03-01
      • 2016-06-04
      • 2014-12-28
      • 2018-03-08
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多