Python asyncio.start\u unix\u服务器和redis的套接字错误

Python asyncio.start\u unix\u服务器和redis的套接字错误,python,python-3.x,redis,python-asyncio,redis-py,Python,Python 3.x,Redis,Python Asyncio,Redis Py,我正在尝试使用asyncio和Unix域套接字在Python中构建一个玩具内存Redis服务器 我的最小示例仅返回每个请求的值baz: 导入异步IO 类服务器: 定义初始化(自): self.server_address=“/tmp/redis.sock” 异步def handle_req(self、reader、writer): 等待读者。readline() writer.write(b“$3\r\nbaz\r\n”) 等待writer.drain() writer.close() 等待编写

我正在尝试使用asyncio和Unix域套接字在Python中构建一个玩具内存Redis服务器

我的最小示例仅返回每个请求的值
baz

导入异步IO
类服务器:
定义初始化(自):
self.server_address=“/tmp/redis.sock”
异步def handle_req(self、reader、writer):
等待读者。readline()
writer.write(b“$3\r\nbaz\r\n”)
等待writer.drain()
writer.close()
等待编写器。等待关闭()
异步def主(自):
server=wait asyncio.start\u unix\u server(self.handle\u req,self.server\u地址)
与服务器异步:
等待服务器。永远为您服务()
def运行(自):
asyncio.run(self.main())
RedisServer().run()
当我使用以下脚本测试两个连续的客户端请求时,它可以工作:

导入时间
导入redis
r=redis.redis(unix_socket_path=“/tmp/redis.sock”)
r、 获取(“foo”)
时间。睡眠(1)
r、 获得(“酒吧”)
但是,如果我删除
time.sleep(1)
,有时它会工作,有时第二个请求会失败,原因是:

回溯(最近一次呼叫最后一次):
文件“/tmp/env/lib/python3.8/site packages/redis/connection.py”,第706行,在send\u packed\u命令中
sendall(自包装,物品)
文件“/tmp/env/lib/python3.8/site packages/redis/_compat.py”,第9行,在sendall中
返回sock.sendall(*args,**kwargs)
断管错误:[Errno 32]断管
在处理上述异常期间,发生了另一个异常:
回溯(最近一次呼叫最后一次):
文件“test.py”,第9行,在
r、 获得(“酒吧”)
get中的文件“/tmp/env/lib/python3.8/site packages/redis/client.py”,第1606行
返回self.execute_命令('GET',name)
文件“/tmp/env/lib/python3.8/site packages/redis/client.py”,第900行,在execute_命令中
conn.send_命令(*args)
文件“/tmp/env/lib/python3.8/site packages/redis/connection.py”,第725行,在send_命令中
self.send_packed_命令(self.pack_命令(*args),
文件“/tmp/env/lib/python3.8/site packages/redis/connection.py”,第717行,在send\u packed\u命令中
引发ConnectionError(“写入套接字时出现错误%s.%s”。%
redis.exceptions.ConnectionError:写入套接字时出现错误32。管道断开。
或:

回溯(最近一次呼叫最后一次):
文件“test.py”,第9行,在
r、 获得(“酒吧”)
get中的文件“/tmp/env/lib/python3.8/site packages/redis/client.py”,第1606行
返回self.execute_命令('GET',name)
文件“/tmp/env/lib/python3.8/site packages/redis/client.py”,第901行,在execute_命令中
返回self.parse\u响应(conn,命令名,**选项)
parse_响应中的文件“/tmp/env/lib/python3.8/site packages/redis/client.py”,第915行
response=connection.read_response()
文件“/tmp/env/lib/python3.8/site packages/redis/connection.py”,第739行,在read_响应中
response=self.\u parser.read\u response()
文件“/tmp/env/lib/python3.8/site packages/redis/connection.py”,第324行,在read_响应中
raw=self.\u buffer.readline()
readline中的文件“/tmp/env/lib/python3.8/site packages/redis/connection.py”,第256行
self.\u从\u套接字()读取\u
文件“/tmp/env/lib/python3.8/site packages/redis/connection.py”,第201行,从插槽读取
raise ConnectionError(服务器\u关闭\u连接\u错误)
redis.exceptions.ConnectionError:服务器已关闭连接。

我的实现似乎缺少客户端库所期望的一些关键行为(可能是由于它是异步的)。我缺少什么?

如果要在每次请求后关闭套接字,则需要使用
write_eof()
,这

刷新缓冲写入数据后,关闭流的写入端

稍微修改一下的代码如下所示:

async def handle_req(self, reader, writer):
    await reader.readline()
    writer.write(b"$3\r\nbaz\r\n")
    await writer.drain()
    writer.write_eof()
    writer.close()
    await writer.wait_closed()
print(r.get("foo"))
print(r.get("bar"))
time.sleep(1)
通常,您不会在每次请求后关闭套接字

以下示例仅用于说明目的,旨在说明无需关闭套接字。当然,您总是会读取一行数据,然后根据Redis协议解释数据。我们知道在这里会发送两个GET命令(每5行,有2个元素的数组的指示符,字符串的指示符,字符串值'GET',还有一个字符串指示符和相应的值,即键)

在客户端发送时,如下所示:

async def handle_req(self, reader, writer):
    await reader.readline()
    writer.write(b"$3\r\nbaz\r\n")
    await writer.drain()
    writer.write_eof()
    writer.close()
    await writer.wait_closed()
print(r.get("foo"))
print(r.get("bar"))
time.sleep(1)
last time.sleep用于确保客户端不会立即退出

控制台上的输出为:

start
b'*2\r\n'
b'$3\r\n'
b'GET\r\n'
b'$3\r\n'
b'foo\r\n'
b'*2\r\n'
b'$3\r\n'
b'GET\r\n'
b'$3\r\n'
b'bar\r\n'

请注意,
start
只输出一次,这表明我们可以处理多个请求,而不必立即关闭套接字。

您的服务器在发送响应后立即关闭套接字。这与真正的Redis服务器的行为匹配吗?如果不匹配,这可能是问题所在-客户端库显然希望c连接以保持打开状态以进行进一步通信。如果我不关闭套接字,则第一个请求永远不会完成。永远不会完成是因为
wait\u closed()
或其他原因?是否应该有一个循环,在服务请求后继续与客户端通信?使用
writer.write\u eof()
在处理每个请求后,而不是关闭套接字,对我来说始终有效。谢谢!