在处理从Websocket服务器推送到我的client.py的数据时,我遇到了很长的(3小时)延迟(编辑:延迟最初是短暂的,然后整天都变长了)。我知道服务器不会延迟它。
例如,每隔5秒我会看到keep_alive日志事件及其相应的时间戳。这样运行平稳。 但是,当我看到日志中处理的数据帧实际上是在服务器发送后的 3小时之后。我是否正在做一些事情来延迟此过程?
我正确地将协程称为'keep_alive'吗? keep_alive只是发送给服务器以保持连接活动的消息。服务器回显该消息。我也记录太多吗?这可能会延迟处理吗(我不这样认为,因为我看到日志记录事件立即发生)。
async def keep_alive(websocket):
"""
This only needs to happen every 30 minutes. I currently have it set to every 5 seconds.
"""
await websocket.send('Hello')
await asyncio.sleep(5)
async def open_connection_test():
"""
Establishes web socket (WSS). Receives data and then stores in csv.
"""
async with websockets.connect(
'wss://{}:{}@localhost.urlname.com/ws'.format(user,pswd), ssl=True, ) as websocket:
while True:
"""
Handle message from server.
"""
message = await websocket.recv()
if message.isdigit():
# now = datetime.datetime.now()
rotating_logger.info ('Keep alive message: {}'.format(str(message)))
else:
jasonified_message = json.loads(message)
for key in jasonified_message:
rotating_logger.info ('{}: \n\t{}\n'.format(key,jasonified_message[key]))
"""
Store in a csv file.
"""
try:
convert_and_store(jasonified_message)
except PermissionError:
convert_and_store(jasonified_message, divert = True)
"""
Keep connection alive.
"""
await keep_alive(websocket)
"""
Logs any exceptions in logs file.
"""
try:
asyncio.get_event_loop().run_until_complete(open_connection())
except Exception as e:
rotating_logger.info (e)
编辑: 从documentation开始-我认为这可能与它有关-但是我还没有联系好点。
max_queue参数设置队列的最大长度 保存传入的消息。默认值为32。0禁用 限制。收到邮件后,邮件就会添加到内存队列中; 然后recv()从该队列中弹出。为了防止过多的记忆 收到消息的速度快于消息的消耗 处理后,队列必须是有界的。如果队列已满,则 协议停止处理传入数据,直到调用recv()为止。在 在这种情况下,各种接收缓冲区(至少在asyncio和 操作系统)将填满,然后TCP接收窗口将缩小,变慢 下行传输以避免丢包。
编辑9/28/2018:我正在测试中没有保持活动消息,这似乎不是问题。可能与convert_and_store()函数有关吗?是否需要先异步定义然后再等待?
def convert_and_store(data, divert = False, test = False):
if test:
data = b
fields = data.keys()
file_name = parse_call_type(data, divert = divert)
json_to_csv(data, file_name, fields)
编辑10/1/2018:似乎保持连接消息和convert_and_store都存在问题;如果我将保持活动消息的时间延长到60秒-那么convert_and_store将每60秒仅运行一次 。因此,convert_and_store正在等待keep_alive()...
答案 0 :(得分:3)
是否可以与convert_and_store()函数相关?
是的,可能是。阻止代码不应直接调用。如果某个函数执行CPU密集型计算1秒钟,则所有异步任务和IO操作都会延迟1秒钟。
执行程序可用于在其他线程/进程中运行阻塞代码:
import asyncio
import concurrent.futures
import time
def long_runned_job(x):
time.sleep(2)
print("Done ", x)
async def test():
loop = asyncio.get_event_loop()
with concurrent.futures.ProcessPoolExecutor() as pool:
for i in range(5):
loop.run_in_executor(pool, long_runned_job, i)
print(i, " is runned")
await asyncio.sleep(0.5)
loop = asyncio.get_event_loop()
loop.run_until_complete(test())
在您的情况下,它应该看起来像这样:
import concurrent.futures
async def open_connection_test():
loop = asyncio.get_event_loop()
with concurrent.futures.ProcessPoolExecutor() as pool:
async with websockets.connect(...) as websocket:
while True:
...
loop.run_in_executor(pool, convert_and_store, args)
已编辑:
保持连接消息和convert_and_store似乎都存在争议
您可以在后台运行keep_alive
:
async def keep_alive(ws):
while ws.open:
await ws.ping(...)
await asyncio.sleep(...)
async with websockets.connect(...) as websocket:
asyncio.ensure_future(keep_alive(websocket))
while True:
...
答案 1 :(得分:1)
您必须确实为此keep_alive()
函数启动一个新线程。
对于async-await
,可以保证在继续下一步之前,所有任务都已完成。
因此,await keep_alive(websocket)
实际上在这种意义上阻塞了线程。您可能不会在这里等待keep_alive
这样的过程才能继续,但是可以肯定的是,那不是您想要的。
实际上,您需要两个时间范围,一个时间用于与服务器通信,一个时间用于使服务器保持活动状态。它们应该分开放置,因为它们位于不同的协程中。
因此,正确的方法是使用Thread
,并且在这种情况下不必使用asyncio
,请保持简单。
首先,将keep_alive()
更改为以下内容。
def keep_alive():
"""
This only needs to happen every 30 minutes. I currently have it set to every 5 seconds.
"""
while True:
websocket.send('Hello')
time.sleep(1)
在open_connection_test()
async def open_connection_test():
"""
Establishes web socket (WSS). Receives data and then stores in csv.
"""
thread = threading.Thread(target=keep_alive, args=())
thread.daemon = True # Daemonize
thread.start()
async with websockets.connect(...) as websocket:
....
#No need this line anymore.
#await keep_alive(websocket)
答案 2 :(得分:0)
我认为这会更清楚,请使用ThreadPoolExecutor
使阻止代码在后台运行
from concurrent.futures import ThreadPoolExecutor
pool = ThreadPoolExecutor(max_workers=4)
def convert_and_store(data, divert=False, test=False):
loop = asyncio.get_event_loop()
loop.run_in_executor(pool, _convert_and_store, divert, test)
def _convert_and_store(data, divert=False, test=False):
if test:
data = b
fields = data.keys()
file_name = parse_call_type(data, divert=divert)
json_to_csv(data, file_name, fields)
asyncio发送保持活动的味精演示
async def kepp_alive(websocket):
while True:
await websocket.send_str(ping)
await asyncio.sleep(10)