异步uvloop介绍及实际应用

1,156 阅读3分钟

这是我参与8月更文挑战的第9天,活动详情查看:8月更文挑战

一、uvloop

是asyncio的事件循环的代替方案。事件循环>默认asyncio事件循环。

pip install uvloop
import asyncio
import uvloop

asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())


# 编写asyncio代码,和之前的一样


async def set_after(fut):
    await asyncio.sleep(2)
    fut.set_result("666")


async def main():
    # 获取当前事件循环
    loop = asyncio.get_running_loop()

    # 创建一个任务(Future对象), 这个任务什么都不干。
    fut = loop.create_future()

    # 创建一个任务(Task对象),绑定set_after函数,函数内部在2s之后,会给fut赋值。
    # 即手动设置future任务的最终结果,fut就可以结束。
    await loop.create_task(set_after(fut))

    # 等待任务最终结果(Future对象),没有结果则会一直等下去。
    data = await fut
    print(data)


# 内部的事件循环会自动变成uvloop
asyncio.run(main())

运行结果如下:

二、实际应用

1、 异步redis

在使用Python代码操作Redis时,连接/操作/断开都是网络IO。

pip install aioredis

示例1:

import asyncio

import aioredis


async def execute(address, password):
    print("开始执行:", address)

    # 网络IO操作:创建Redis连接
    # 本人Redis没有设置密码,所以连接时不设置password
    # redis = await aioredis.create_redis(address, password=password)
    redis = await aioredis.create_redis(address)

    # 网络IO操作:在Redis中设置hash值car, 内部在设置三个键值对,即:Redis = {car: {key1:1, key2:2, key3:3}}
    await redis.hmset_dict('car', key1=1, key2=2, key3=3)

    result = await redis.hgetall('car', encoding='utf-8')
    print(result)

    redis.close()
    # 网络操作:关闭Redis
    await redis.wait_closed()

    print("结束:", address)


asyncio.run(execute('redis://127.0.0.1:6379', ""))

运行结果如下:

示例2:

import asyncio

import aioredis


async def execute(address, password):
    print("开始执行:", address)

    # 本人Redis没有设置密码,所以连接时不设置password
    # redis = await aioredis.create_redis(address, password=password)
    # 网络IO操作:先去连接127.0.0.1:6379, 遇到IO则自动切换任务,去连接127.0.0.1:6380
    redis = await aioredis.create_redis(address)

    # 网络IO操作:遇到IO自动切换任务
    await redis.hmset_dict('car', key1=1, key2=2, key3=3)

    # 网络IO操作:遇到IO自动切换任务
    result = await redis.hgetall('car', encoding='utf-8')
    print(result)

    redis.close()
    # 网络IO操作:遇到IO自动切换任务
    await redis.wait_closed()

    print("结束:", address)


task_list = [
    execute('redis://127.0.0.1:6379', ""),
    execute('redis://127.0.0.1:6380', ""),
]
asyncio.run(asyncio.wait(task_list))

运行结果如下:

2、异步MySQL

pip install aiomysql

示例1:

import asyncio
import aiomysql


async def execute():
    print("开始执行:")

    # 网络IO操作:连接MySQL
    conn = await aiomysql.connect(host='127.0.0.1', port=3306, user='root', password='root', db='mysql', )

    # 网络IO操作:创建cursor
    cur = await conn.cursor()

    # 网络IO操作:执行SQL
    await cur.execute('select Host, User from user')

    # 网络IO操作:获取SQL结果
    result = await cur.fetchall()
    print(result)

    # 网络IO操作:关闭连接
    await cur.close()
    conn.close()


asyncio.run(execute())

运行结果如下:

示例2:

import asyncio
import aiomysql


async def execute(host, password):
    print("开始执行:", host)

    # 网络IO操作:先去连接127.0.0.1:3306,遇到IO则自动切换,去连接154.8.147.238:3306
    conn = await aiomysql.connect(host=host, port=3306, user='root', password=password, db='mysql', )

    # 网络IO操作:遇到IO会自动切换任务
    cur = await conn.cursor()

    # 网络IO操作:遇到IO会自动切换任务
    await cur.execute('select Host, User from user')

    # 网络IO操作:遇到IO会自动切换任务
    result = await cur.fetchall()
    print(result)

    # 网络IO操作:遇到IO会自动切换任务
    await cur.close()
    conn.close()


task_list = [
    execute('127.0.0.1', "root"),
    execute('154.8.147.238', "root"),
]
asyncio.run(asyncio.wait(task_list))

运行结果如下:

3、FastAPI框架

安装

pip install fastapi
pip install uvicorn (asgi内部基于uvloop)

示例:

import asyncio

import aioredis
import uvicorn
from fastapi import FastAPI
from redis import Redis

app = FastAPI()

# 创建一个Redis连接池
REDIS_POOL = aioredis.ConnectionsPool('redis://127.0.0.1:6379', minsize=1, maxsize=10)


@app.get('/')
def index():
    '''普通操作接口'''
    return {"message": "hello word"}


@app.get('/red')
async def red():
    '''异步操作连接'''

    print('请求来了')

    await asyncio.sleep(3)
    # 连接池获取一个连接
    conn = await REDIS_POOL.acquire()
    redis = Redis(conn)

    # 设置值
    await redis.hmset_dict('car', key1=1, key2=2, key3=3)

    # 读取值
    result = await redis.hgetall('car', encoding='utf-8')
    print(result)

    # 连接归还连接池
    REDIS_POOL.release(conn)

    return result


if __name__ == "__main__":
    uvicorn.run("luffy:app", host='127.0.0.1', port=5000, log_level='info')

4、爬虫

pip install aiohttp
import aiohttp
import asyncio


async def fetch(session, url):
    print("发送请求:", url)
    async with session.get(url, verify_ssl=False) as response:
        text = await response.text()
        print("得到结果:", url, len(text))
        return text


async def main():
    async with aiohttp.ClientSession() as session:
        url_list = [
            'https://img.tupianzj.com/uploads/allimg/180704/9-1PF4222401.jpg',
            'https://img.tupianzj.com/uploads/allimg/180127/9-1P12G03548.jpg',
            'https://img.tupianzj.com/uploads/allimg/190329/34-1Z32Z93602.jpg',
            'https://img.tupianzj.com/uploads/allimg/160229/9-160229100600.jpg',
            'https://img.tupianzj.com/uploads/allimg/190430/34-1Z4301F923-50.jpg',
            'https://img.tupianzj.com/uploads/allimg/202001/9999/fe8c80add1.jpg'
        ]

        tasks = [asyncio.create_task(fetch(session, url) for url in url_list)]

        done, pending = await asyncio.wait(tasks)
        print(done)


if __name__ == "__main__":
    asyncio.run(main())

三、总结

最大的意义:通过一个线程利用其IO等待时间去做一些其他的事情。