如何运行一个没有结束而没有等待结果的协程?

时间:2017-03-19 05:10:34

标签: python python-3.x python-asyncio python-3.6

我有这段代码,我想连接到websocket并通过发送心跳来保持连接活跃。在执行此操作时,我还希望能够将有效负载发送到websocket,但是我一直卡在等待keepAlive协程的结果

import asyncio
import websockets
ws=#path goes here
async def sendHeartbeat(socket):
    message={
        "op": 1,
        "d": 0
        }
    await socket.send(json.dumps(message))
    print("heartbeat sent")


async def keepAlive(socket):
    while 1==1:
        await sendHeartbeat(socket)
        resp=await(socket.recv())
        print(resp)
        if json.loads(resp)["op"]==11:
            #the server sent an ack back
            print("ack received")
        else:
            socket.close()
        await asyncio.sleep(10)




async def beginSocket(loop):
    async with websockets.connect(ws) as socket:
        print(await socket.recv())
        await keepAlive(socket)
        #payloads would be sent here
        print("Hey payloads being sent and stuff")

loop = asyncio.get_event_loop()
loop.run_until_complete(beginSocket(loop))

但是,使用这段代码,等待keepAlive后的打印语句永远不会打印出来。我怎样才能使代码不等待keepAlive的结果?

2 个答案:

答案 0 :(得分:0)

虽然keepAlive(socket)会立即返回,但由于keepAlive是一个couroutine,await keepAlive(socket)永远不会返回,因为keepAlive()包含无限循环。

不要使用await,请尝试asyncio.ensure_future(keepAlive(socket))

如果您确实想使用await keepAlive(socket),请尝试从其他地方发送有效负载(可能事先使用asyncio.ensure_future(send_payload(socket)))。

答案 1 :(得分:0)

在这种情况下,您在概念上通过同一套接字进行两次单独的对话。一个对话就是你的心跳和回复信息。另一个是您要发送的其他数据包。

我会保留3个独立的顶级(即他们直接向事件循环报告)任务。我希望他们都能引用一些协调员,所以当有时间时,他们中的任何一个都可以取消所有其他协调员。

  1. 心跳任务,基本上是您的keepAlive功能。
  2. 处理你在websocket上进行的其他对话的任务。
  3. 多路复用从websocket读取的任务。
  4. 多路复用任务的工作是将消息路由到适当的任务。心跳任务应该只获得心跳响应,而另一个任务应该获取所有其他消息。

    由于websockets已经对消息进行了框架处理,因此您只能sendrecv整条消息,因此它可能拥有的另一项工作无关紧要。

    这是你可以写这个的一种方式。

    import asyncio
    import websockets
    ws=#path goes here
    
    class RoutingTask(object):
        def __init__(self, sock, defaultQueue, **kwargs):
            super().__init__(**kwargs)
            self.sock = sock
            self.defaultQueue = defaultQueue # The queue all messages not otherwise matched go to.
            self.matchers = []
    
        async def run(self):
            while True:
                msg = await self.sock.recv()
                msg = json.loads(msg)
                matched = False
                for matcher, queue in matchers:
                    if matcher(msg):
                        await queue.put(msg)
                        matched = True
                        break
                if not matched:
                    await self.defaultQueue.put(msg)
    
        def addMatcher(self, matcher, queue):
            el = (matcher, queue)
            self.matchers.append(el)
    
    async def heartbeatTask(wssock, incomingq):
        message=json.dumps({
            "op": 1,
            "d": 0
            }) # Do this just once.
        while True:
           await wssock.send(message)
           print("heartbeat sent")
           response = await asyncio.wait_for(incomingq.get(), 10) # Wait 10 seconds for response.
           assert response['op'] == 11
           print("heartbeat response received.")
           await asyncio.sleep(10) # Wait 10 seconds to send another heartbeat.
    
    async def beginSocket(loop):
        def heartbeatMatcher(jsondict):
            return jsondict.get('op', None) == 11
    
        async with websockets.connect(ws) as socket:
            myq = asyncio.Queue(maxsize=1)
            heartbeatq = asyncio.Queue(maxsize=1)
            router = RoutingTask(socket, myq)
            router.addMatcher(heartbeatMatcher, heartbeatq)
            router = asyncio.ensure_future(router.run())
            heartbeat = asyncio.ensure_future(heartbeatTask(socket, heartbeatq)
    
            print(await myq.get())
            #payloads would be sent here
            print("Hey payloads being sent and stuff")
    
        heartbeat.cancel() # Stop the heartbeat
        router.cancel() # Stop the router task
    
    loop = asyncio.get_event_loop()
    loop.run_until_complete(beginSocket(loop))
    

    这里有一些问题。如果抛出异常,heartbeatrouter任务可能不会被取消。他们也没有很好的方法将问题报告回主beginSocket任务。这基本上是一种快速和肮脏的一次性,以演示如何做你想做的事。

    在我看来,asyncio.ensure_future被错误命名。它的作用是告诉事件循环它需要保持运行的新事物。它基本上是启动了一个线程的协同程序。