Python异步IO队列get不';我没有收到消息
我发布了一个与get from队列相关的新问题。 这是代码(感谢Martijn Pieters) 此代码是由从父进程调用的子进程Python异步IO队列get不';我没有收到消息,python,python-3.x,asynchronous,queue,python-asyncio,Python,Python 3.x,Asynchronous,Queue,Python Asyncio,我发布了一个与get from队列相关的新问题。 这是代码(感谢Martijn Pieters) 此代码是由从父进程调用的子进程 subprocess.Popen(["python3", test], stdin=subprocess.PIPE, stdout=subprocess.PIPE, bufsize=2048) 问题是socket\u consumer(从套接字接收)将对象放入队列中,但pipe\u producer不会从incoming.get()开始。 文件写入仅用于测试目的 目
subprocess.Popen(["python3", test], stdin=subprocess.PIPE, stdout=subprocess.PIPE, bufsize=2048)
问题是socket\u consumer
(从套接字接收)将对象放入队列中,但pipe\u producer
不会从incoming.get()
开始。
文件写入仅用于测试目的
目前的家长是这样的(仅用于测试)
相反,要发送到web套接字,我使用以下代码:
#!/usr/bin/env python
import asyncio
import websockets
async def hello(uri):
header = {"Authorization": r"Basic XXXX="}
message = '{"header":{"protocolVersion":1,"messageID":2,"stationID":400},"cam":{"generationDeltaTime":1,"camParameters":{"basicContainer":{"stationType":5,"referencePosition":{"latitude":451114425,"longitude":76720957,"positionConfidenceEllipse":{"semiMajorConfidence":4095,"semiMinorConfidence":4095,"semiMajorOrientation":3601},...other fields}}';
async with websockets.connect(uri, extra_headers=header) as websocket:
await websocket.send(message)
asyncio.get_event_loop().run_until_complete(
hello('XXX'))
它通过管道发送并工作,因为我通过管道接收并发送到套接字(pipe.txt和ToSocket.txt中的文件是正确的)。然后,我将代码发送到具有打开的web套接字的服务器,该服务器将消息发送给子服务器。当孩子从套接字接收文件时,会创建FromSocket.txt文件,但直到我将ToPipe.txt文件放在
awit incoming.get()之前,才会创建ToPipe.txt文件
FromSocket.txt
包含以下内容:
From socket: '{"header":{"protocolVersion":1,"messageID":2,"stationID":400},"cam":{"generationDeltaTime":1, ... other field}}'
但是,如果类型检索出现问题,它将创建文件,因为它是json\u message=wait incoming.get()之后的第一条指令。
我认为这是排队的问题。
对于测试,我在waitouting.put(message)
之后,将incoming.get()
放入socket\u消费者中,它就工作了
更新:如果我只运行子级(因此没有管道),ToPipe.txt是正确的,消息transfert from socket to pipe是正确的。
对于我的测试,我运行父级,它向管道发送一条消息,子级发送到套接字,然后我向套接字发送一条消息,子级捕获该消息,但它不发送到管道,并且不会创建ToPipe.txt。可能主方法中有问题您正在向子进程写入双编码JSON:
message = '{"header":{"protocolVersion":1,"messageID":2,"stationID":400}, the rest of json...}}';
jsonValue = json.dumps(message)
message
已经是一个JSON字符串,因此jsonValue
是一个双重编码的JSON字符串
管道使用者将此双重编码字符串推送到套接字队列中。接下来,socket\u producer()
中的websocket producer再次对消息进行编码:
while True:
message = await incoming.get()
# ...
json_message = json.dumps(message)
await socket.send(json_message)
所以现在json\u message
是一个三重编码的json值,一个json文档包含一个json文档,其中包含一个json文档:
>>> import json
>>> message = '{"header":{"protocolVersion":1,"messageID":2,"stationID":400}}}' # valid JSON
>>> json_message = json.dumps(message)
>>> print(json_message) # double-encoded
"{\"header\":{\"protocolVersion\":1,\"messageID\":2,\"stationID\":400}}}"
>>> json_message = json.dumps(json_message) # encode *again*
>>> print(json_message) # triple-encoded
"\"{\\\"header\\\":{\\\"protocolVersion\\\":1,\\\"messageID\\\":2,\\\"stationID\\\":400}}}\""
message = json.loads(json_message)
type = int(message.get('header', {}).get('messageID', -1))
我不知道您的web套接字对此做了什么,但是让我们假设它使用json.loads()
一次,然后回显解码后的消息。这意味着,socket\u consumer()
只接收两次编码的JSON文档。您的FromSocket.txt
日志肯定意味着这就是发生的情况,因为它包含一条双重编码的JSON消息:
您可以在FromSocket.txt
日志中看到这一点:
From socket: "{\"header\":{\"protocolVersion\":1,\"messageID\":2,\"stationID\":400},\"cam\":{\"generationDeltaTime\":1,...other fields}}"
请注意那些\”
条目,整个文档都用引号括起来,但值中没有三个反斜杠
尽管如此,JSON编码的这种额外分层破坏了管道\u producer()
协同程序,它期望消息解码到字典,而不是另一个字符串(即使该字符串包含另一个JSON文档):
message
将改为解码为字符串,因此message.get
将失败,出现AttributeError
,导致协同程序退出:
>>> json_message = "{\"header\":{\"protocolVersion\":1,\"messageID\":2,\"stationID\":400}}}" # double encoded
>>> message = json.loads(json_message)
>>> message # Back one stop, single-encoded JSON
'{"header":{"protocolVersion":1,"messageID":2,"stationID":400}}}'
>>> type(message) # it's a string with JSON, not a dictionary
<class 'str'>
>>> message.get('header')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'str' object has no attribute 'get'
您可能希望记录解码在某个地方失败(将消息推送到一个日志队列,由一个单独的任务选择写入日志)
接下来,我们可以更新connect.*
函数,以不忽略完成的任务中的异常:
done, pending = await asyncio.wait(
[consumer_task, producer_task], return_when=asyncio.FIRST_COMPLETED)
for task in pending:
task.cancel()
# force a result check; if there was an exception it'll be re-raised
for task in done:
task.result()
done.result()
检查可以重新引发消费者或生产者中抛出的异常。由于connect.*
协同路由是通过asyncio.gather()
运行的,而异步io.gather()
反过来又由循环运行。运行直到\u complete()
,因此该异常会一直传播到main()
函数,因此它将退出Python,您可以看到打印的回溯。我更新了另一个答案,将任务的包含在done:task.result()中,因为这是一个很好的实践
只需在我的原始答案代码中使用task.result()
循环,以及一个只回显消息的websocket,并输入一个有效的JSON文档(不是双重编码),我可以立即看到错误;这里的父进程是我的终端,所以我只是将JSON消息复制到我的终端窗口中,以便将数据发送到管道中:
$ python3.7 so52291218.py
{"header":{"protocolVersion":1,"messageID":2,"stationID":400}}
Traceback (most recent call last):
File "so52291218.py", line 140, in <module>
main()
File "so52291218.py", line 137, in main
loop.run_until_complete(asyncio.gather(socket_coro, pipe_coro))
File "/.../lib/python3.7/asyncio/base_events.py", line 568, in run_until_complete
return future.result()
File "so52291218.py", line 126, in connect_pipe
task.result()
File "so52291218.py", line 104, in pipe_producer
type = int(message.get("header", {}).get("messageID", -1))
AttributeError: 'str' object has no attribute 'get'
因此,每次我们向管道发送一条完整的线路时,我们也会在短时间内寻找另一条线路,并回送任何此类线路
所有修复都已就绪(确保避免多次编码JSON消息),并且非常简单,上面的pexpect
代码将打印:
>>> {"header":{"protocolVersion":1,"messageID":2,"stationID":400}}
>>> {"header":{"protocolVersion":1,"messageID":2,"stationID":400}}
>>> {"header":{"protocolVersion":1,"messageID":2,"stationID":400}}
>>> {"header":{"protocolVersion":1,"messageID":2,"stationID":400}}
>>> {"header":{"protocolVersion":1,"messageID":2,"stationID":400}}
显示从父进程到子进程到websocket再到websocket有一个完整的往返路径。您是如何从父进程Popen读写的?您使用的是select
还是多线程?您可以尝试使用吗?这样可以更容易地确保iss不是父进程下一步,我们需要更多关于此处交换的实际消息的详细信息,以确保pipe\u producer
解析消息的方式没有问题。能否显示ToPipe.txt
输出以及父进程和websocket正在发送和接收的数据?我添加了更多详细信息您从未从管道stdin读取数据。这可能会导致整个I/O层完全暂停,并且您的写入操作也不会进入子进程。请务必在此处使用pexpect
,以避免类似的问题。我在下面的回答中概述了还有哪些问题。问题在于,您从未指定管道和WebSock的数据类型t发送和接收;我认为您已经解决了这些部分。感谢您的解释,我删除了json编码
done, pending = await asyncio.wait(
[consumer_task, producer_task], return_when=asyncio.FIRST_COMPLETED)
for task in pending:
task.cancel()
# force a result check; if there was an exception it'll be re-raised
for task in done:
task.result()
$ python3.7 so52291218.py
{"header":{"protocolVersion":1,"messageID":2,"stationID":400}}
Traceback (most recent call last):
File "so52291218.py", line 140, in <module>
main()
File "so52291218.py", line 137, in main
loop.run_until_complete(asyncio.gather(socket_coro, pipe_coro))
File "/.../lib/python3.7/asyncio/base_events.py", line 568, in run_until_complete
return future.result()
File "so52291218.py", line 126, in connect_pipe
task.result()
File "so52291218.py", line 104, in pipe_producer
type = int(message.get("header", {}).get("messageID", -1))
AttributeError: 'str' object has no attribute 'get'
import sys
import pexpect
test = '...'
process = pexpect.popen_spawn.PopenSpawn([sys.executable, test])
for i in range(5):
message = '{"header":{"protocolVersion":1,"messageID":2,"stationID":400}}';
jsonValueBytes = message.encode("utf-8")
process.send(jsonValueBytes + b"\n")
# echo anything coming back
while True:
index = process.expect([process.crlf, pexpect.EOF, pexpect.TIMEOUT], timeout=0.1)
if not process.before:
break
print('>>>', process.before.decode('utf8', errors='replace'), flush=True)
# send EOF to close the pipe, then terminate the process
process.sendeof()
process.kill(1)
process.wait()
>>> {"header":{"protocolVersion":1,"messageID":2,"stationID":400}}
>>> {"header":{"protocolVersion":1,"messageID":2,"stationID":400}}
>>> {"header":{"protocolVersion":1,"messageID":2,"stationID":400}}
>>> {"header":{"protocolVersion":1,"messageID":2,"stationID":400}}
>>> {"header":{"protocolVersion":1,"messageID":2,"stationID":400}}