使用asyncio将多个用户登录到服务器

我有一台服务器,我需要登录许多用户 . 在每次登录期间,服务器必须做很多事情,例如向其他服务器/数据库发出请求并等待它们 . 因此,当一个用户登录时,我希望其他用户也开始登录 .

我试图弄清楚asyncio模块是如何工作的,但是没有找到任何代码来完成我想要它做的事情 .

注意:我使用的是python 3.6,因此一些asyncio功能与3.7不同 .

import asyncio
import requests

class user:
    def __init__(self, port):
        # Create user here
        self.port=port
        self.data = requests.get(someURL,port)

    async def login(self,email):
        # Step 1 of logging in
        requsts.post(someURL, self.port, email) # Logs them in but takes a while to process everything
        while True:
            data = requests.get(someURL, self.port)# Gets data from server
            if data.connected == True:
                break
            else:
                pass
                #Go onto logging in next user while we wait for the server to do things

        # Step 2 of logging in
        requests.post(someURL, self.port, email)
        while True:
            data = requests.get(someURL, self.port)
            if data.loggedIn == True:
                break
            else:
                pass
                #Go onto logging in next user while we wait for the server to do things

listOfUsers = []
for i in range(3):
    listOfUsers.append(user(3000+i)) # Creates 3 users

async def loginListOfUsers():

    for user in users:
        await user.login(user.port + "@gmail.com") # Logs in 3 users with i@gmail.com


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

我想知道:

  • asyncio是我正在尝试做的正确工具

  • 如何使用asyncio来完成它

这是我想要/认为asyncio可以做的:

创建一个事件循环,

将协同程序添加到此循环并运行它们,

当它到达某一点时,例如一个await语句,它停止运行该协同程序并转到循环中的下一个,将该一个推送到循环的后面

我对asyncio没有很好的理解,所以我很错误,但希望你能理解我的问题是什么 .

回答(2)

3 years ago

当它到达某一点时,例如一个await语句,它停止运行该协同程序并转到循环中的下一个,将该一个推送到循环的后面

这正是asyncio擅长的 . 要并行执行任务,您的代码需要满足两个先决条件:

  • 它实际上需要异步;

  • 它需要请求协同程序并行运行而不是等待它们串联 .

如果您仔细查看问题中协程的内容,例如 user.login ,第一点就变得清晰了 . 虽然它们在技术上是协程(它们都以 async def 开头),但它们并没有任何东西,因此它们永远不会为asyncio提供暂停执行以执行其他操作的机会 .

为了使它们有用,您需要使用非阻塞等效替换每个阻塞代码的调用 . 在使用 requests 时,最简单的帮助是使用run_in_executor将执行推迟到另一个线程 . 换句话说,替换:

data = requests.get(someURL, self.port)

data = await loop.run_in_executor(None, requests.get, someURL, self.port)

通过简单的更改 loginListOfUsers 可以满足第二点 . 它不是等待串联的每个协程(基本上确保它们不会并行运行),而是需要提前启动它们并等待它们完成,例如使用 asyncio.gather

async def loginListOfUsers():
    # assemble a list of coroutines without actually running them
    todo = [user.login(user.port + "@gmail.com")
            for user in users]
    # now let all the coroutines run in parallel,
    # and wait for them to complete
    await asyncio.gather(*todo)

请注意,使用 run_in_executor 将使用引擎下的线程,并且只允许与线程池中的工作程序一样多的并行连接 . 为了更好地使用asyncio,您可以切换到支持多个连接的aiohttp,而不是每个连接都由OS线程支持 .

3 years ago

python中有两种库 . 异步库和阻塞库 . 使用asyncio时,您应该只尝试使用asyncio库 .

请求是一个阻止libarie . 这是一个例子,如何与asyncio一起使用:https://stackoverflow.com/a/23784727/4493241

我根本不喜欢使用阻塞库 . 因此,我建议您在没有请求的情况下重写代码 . 这是一个带有asyncio的http客户端示例:https://asyncio.readthedocs.io/en/latest/http_client.html