Python:如何等到子进程完成初始化

时间:2017-07-20 13:30:52

标签: python sockets server redis subprocess

我正在尝试为单元测试创​​建一个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())
一切正常。显然,服务器尚未准备好接受没有睡眠的连接。

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

2 个答案:

答案 0 :(得分:1)

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

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对象

答案 1 :(得分:0)

您使用Popen启动redis服务器。这需要花费时间(在您的情况下为0.01秒),因为打开了进程并与您的接口进行了监听连接。 Popen是非阻塞的,因此命令将在您的系统上设置,代码继续。这是with语句在redis-server启动之前完成的时间。我认为你必须在启动redis服务器后在阻塞调用中使用redis-cli pingsubprocess.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'])