【问题标题】:Asyncio: Start a non-blocking listening serverAsyncio:启动一个非阻塞监听服务器
【发布时间】:2015-07-31 09:09:47
【问题描述】:

这是来自 asyncio tutotial 的基本 tcp 服务器:

import asyncio

class EchoServerClientProtocol(asyncio.Protocol):
    def connection_made(self, transport):
        peername = transport.get_extra_info('peername')
        print('Connection from {}'.format(peername))
        self.transport = transport

    def data_received(self, data):
        message = data.decode()
        print('Data received: {!r}'.format(message))

        print('Send: {!r}'.format(message))
        self.transport.write(data)

        print('Close the client socket')
        self.transport.close()

loop = asyncio.get_event_loop()
# Each client connection will create a new protocol instance
coro = loop.create_server(EchoServerClientProtocol, '127.0.0.1', 8888)
server = loop.run_until_complete(coro)

# Serve requests until CTRL+c is pressed
print('Serving on {}'.format(server.sockets[0].getsockname()))
try:
    loop.run_forever()
except KeyboardInterrupt:
    pass

# Close the server
server.close()
loop.run_until_complete(server.wait_closed())
loop.close()

与所有(我发现的)其他示例一样,它使用阻塞 loop.run_forever()。
我如何开始列出服务器并及时做其他事情?
我试图在一个函数中外包启动服务器并使用 asyncio.async() 启动这个函数,但没有成功。 我在这里缺少什么?

【问题讨论】:

  • 你还想做什么?通常,您的应用程序所做的一切都发生在内部事件循环中;因此,除了启动服务器之外,在调用loop.run_forever 之前,您还可以使用loop.call_soon 在循环开始之前安排回调。这会涵盖您的用例,还是您想运行一些根本不与asyncio 交互的其他代码?
  • 从这个问题中并不清楚你想做的“其他事情”是什么。您需要在上面包含的示例代码中添加什么?
  • 我的基本问题是:如何使用 python3 启动非阻塞侦听服务器以通过网络进行客户端-服务器通信?我的第一种方法是启动第二个进程,一直监听并在 multiprocessing.queue 中写入传入消息。但是(我想)也许有一个库可以让它更容易和“更好”?

标签: python python-asyncio


【解决方案1】:

您可以在调用loop.run_forever() 之前安排多个并发异步任务。

@asyncio.coroutine
def other_task_coroutine():
   pass  # do something

start_tcp_server_task = loop.create_task(loop.create_server(
    EchoServerClientProtocol, '127.0.0.1', 8888))

other_task = loop.create_task(other_task_coroutine())

self.run_forever()

当您调用loop.create_task(loop.create_server())loop.create_task(other_task_coroutine()) 时,什么都没有实际上被执行:一个协程对象被创建并包装在一个任务中(将一个任务视为一个外壳,而协程是一个将在任务中执行的代码)。任务在创建时被安排在循环中。

循环将首先执行start_tcp_server_task(因为它首先被调度),直到阻塞 IO 事件未决或被动套接字准备好侦听传入连接。

您可以将 asyncio 视为在一个 CPU 上运行的不可抢占式调度程序:一旦第一个任务自行中断或完成,第二个任务将被执行。因此,当执行一项任务时,另一项必须等到正在运行的任务完成或产生(或在 Python 3.5 中“等待”)。 “yielding”(yield from client.read())或“awaiting”(await client.read())意味着任务将手交还给循环的调度程序,直到可以执行client.read()(数据在套接字上可用)。

一旦任务将控制权交还给循环,它就可以调度其他挂起的任务、处理传入事件并调度等待这些事件的任务。一旦无事可做,循环将执行进程的唯一阻塞调用:休眠,直到内核通知它事件已准备好处理。

在这种情况下,您必须了解,在使用 asyncio 时,进程中运行的所有内容都必须异步运行,这样循环才能完成其工作。您不能在循环中使用多处理对象。

注意asyncio.async(coroutine(), loop=loop) 等同于loop.create_task(coroutine())

【讨论】:

  • 这并不是我所希望的,但感谢您的澄清。我想我会使用 rabbitmq/pika 来达到我的目的
【解决方案2】:

此外,您可以考虑在执行程序中运行您想要的内容。 例如。

coro = loop.create_server(EchoServerClientProtocol, '127.0.0.1', 8888)
server = loop.run_until_complete(coro)
async def execute(self, loop):
    await loop.run_in_executor(None, your_func_here, args: 

asyncio.async(execute(loop))
loop.run_forever()

执行器将在执行器中运行您想要的任何功能,这不会阻塞您的服务器。

【讨论】:

    猜你喜欢
    • 2014-03-28
    • 2014-03-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-03-23
    • 1970-01-01
    • 2015-04-25
    • 1970-01-01
    相关资源
    最近更新 更多