asyncio协程异步实战

经典案例

import asyncio

async def print_hello():
    while 1:
        print('hello_word')
        await asyncio.sleep(1)    #这个sleep不同time.sleep  这个sleep是让出线程控制权,给事件循环中别的协程

async def print_googbye():
    while 1:
        print('goodbye')
        await asyncio.sleep(2)		#这个相当于模拟IO阻塞操作,让出线程控制权,交由事件循环中别的协程来处理

c1 = print_hello()
c2 = print_googbye()            #这个相当于是创建协程对象
loop = asyncio.get_event_loop()         #获取事件循环
loop.run_until_complete(asyncio.gather(c1,c2))  #吧协程对象添加到事件循环中去执行

网络请求的异步化
实例1

import asyncio
import random

async def cronschedule():
    page = 1
    await asyncio.sleep(1)
    while 1:
        job = cron_job(f'https://www.baidu.com/{page}')
        await job			#走到这里,需要等待job执行结束,才可以执行下一次
        page += 1			#这样写就阻塞线程,不能达到高并发的目的
        

async def cron_job(url):
    n = random.randint(1,3)
    await asyncio.sleep(n)
    print('下载结束:' + url)

asyncio.run(cronschedule())

实例二

import asyncio
import random

async def cronschedule():
    page = 1
    await asyncio.sleep(1)
    while 1:
        job = cron_job(f'https://www.baidu.com/{page}')
        asyncio.create_task(job)  		#注册到事件循环,我们必须将新协程分离出去,让它和协程并发
        #a = asyncio.ensure_future(job) #如果你对job的协程对象的返回值需要,则需要用这个方法
        await asyncio.sleep(0.01)  		#必须有await,交出线程控制权,不然死循环会一直占用线程
        page += 1

async def cron_job(url):
    n = random.randint(1,3)
    await asyncio.sleep(n)
    print('下载结束:' + url)

asyncio.run(cronschedule())		#肉眼可见的并发数很高

案例三

import asyncio
import functools
import random

def on_job_down(url,task):
    print('下载结束',url,task.result()) #task.result 可以拿到cron_job协程函数的返回值


async def cronschedule():
    page = 1
    await asyncio.sleep(1)
    while 1:
        url = f'https://www.baidu.com/{page}'
        job = cron_job(url)
        task = asyncio.create_task(job)  #注册到事件循环,我们必须将新协程分离出去,让它和协程并发
        #a = asyncio.ensure_future(job) #如果你对job的协程对象的返回值需要,则需要用这个方法
        task.add_done_callback(functools.partial(on_job_down,url)) #这个是回调函数
        await asyncio.sleep(0.01)  #必须有await,交出线程控制权,不然死循环会一直占用线程
        page += 1

async def cron_job(url):
    n = random.randint(1,3)
    await asyncio.sleep(n)
    print('下载结束:' + url)
    return '<html></html>'

asyncio.run(cronschedule())

异步和非异步模块混合案例

网络请求下载案例

import asyncio
import time

async def down_load(url):
    #发起网络请求、下载图片(遇到网络下载图片的IO请求,自动化切换到其他任务)
    print('开始下载')
    loop = asyncio.get_event_loop()

    #requests模块默认不支持异步操作,所以使用线程池来配合实现
    future = loop.run_in_executor(None,requests.get,url)
    response = await future
    print('下载完成')

    #图片保存本地
    file_name = time.time()
    with open(f'{file_name}.jpg','wb') as f:
        f.write(response.content)

if __name__ == '__main__':
    url_list = [
        'https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=2229640758,955716948&fm=26&gp=0.jpg',
        'https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=2637673594,2560483073&fm=26&gp=0.jpg',
        'https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=2393851800,1908382647&fm=26&gp=0.jpg'
    ]

    tasks = [down_load(url) for url in url_list]
    asyncio.run(asyncio.wait(tasks))
    # loop = asyncio.get_event_loop()
    # loop.run_until_complete(asyncio.wait(tasks))

异步操作Redis

在通过python代码操作redis时,连接/操作/断开都是网络IO。

案例1

pip install aioredis				--支持异步操作的模块



import asyncio
import aioredis

async def execute(address,password):
    print('开始执行',address)
    #网络IO操作,创建redis连接
    redis = await aioredis.create_redis(address,password=password)

    #网络IO操作,存数据,即 redis = {car:{key1:1,key2:2,key3:3}}
    await redis.hmset_dict('car',key1=1,key2=2,key3=3)

    #网络IO操作,去redis中获取值
    result = await redis.hgetall('car',encoding='utf-8')
    print(result)

    redis.close()
    #网络IO操作,关闭redis连接
    await redis.wait_closed()
    print('结束',address)

asyncio.run(execute('redis://47.93.4.198:6379','密码'))

案例2

import asyncio
import aioredis

async def execute(address,password):
    print('开始执行',address)
    #网络IO操作,创建redis连接
    redis = await aioredis.create_redis(address,password=password)

    #网络IO操作,存数据,即 redis = {car:{key1:1,key2:2,key3:3}}
    await redis.hmset_dict('car',key1=1,key2=2,key3=3)

    #网络IO操作,去redis中获取值
    result = await redis.hgetall('car',encoding='utf-8')
    print(result)

    redis.close()
    #网络IO操作,关闭redis连接
    await redis.wait_closed()
    print('结束',address)

task_list = [
    execute('redis://47.93.4.198:6379','密码'),			#连接两个redis
    execute('redis://47.93.4.197:6379','密码')
]

asyncio.run(asyncio.wait(task_list))
#连接第一个redis遇到网络IO,会自动切换到第二个连接

异步操作Mysql

示例1

pip install aiomysql         -- 支持mysql的异步的模块


import asyncio
import aiomysql

async def execute(address,password):
    print('开始执行',address)
    #网络IO操作,创建redis连接
    conn = await aiomysql.connect(host='127.0.0.1',port=3306,user='root',password='123')

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

    #网络IO操作,执行SQL结果
    await cur.execute('SELECT HOST,USER FROM USER')

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

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

asyncio.run(execute())

案例2(连接多个mysql)

import asyncio
import aiomysql

async def execute(host,password):
    print('开始执行',host)
    #网络IO操作,创建redis连接
    conn = await aiomysql.connect(host=host,port=3306,user='root',password=password,db='mysql')

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

    #网络IO操作,执行SQL结果
    await cur.execute('SELECT HOST,USER FROM USER')

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

    #网络IO操作,关闭连接
    await cur.close()
    conn.close()
    print('结束',host)
    
task_list = [
    execute('47.93.41.197','密码'),
    execute('47.93.41.198','密码')
]

asyncio.run(asyncio.wait(task_list))

异步FastApi框架

pip install fastapi uvicorn -i https://pypi.doubanio.com/simple     -- 装模块

案例1

import asyncio
import aioredis
import uvicorn
from fastapi import FastAPI

app = FastAPI()

#创建redis连接池
REDIS_POOL = aioredis.ConnectionsPool('redis://47.193.14.198:6379',password='密码',minsize=1,maxsize=10)

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

@app.get('/red')
async def red():
    '''异步操作接口'''
    print('请求来了')
    await asyncio.sleep(3)
    #连接池获取一个连接
    conn = await REDIS_POOL.acquire()
    redis = aioredis.Redis(conn)

    #存值
    await redis.hmset_dict('car',key1=1,key2=2)

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

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

if __name__ == '__main__':
    uvicorn.run('1:app',host='127.0.0.1',port=5000,log_level='info')
    #这个1的意思,是这个文件的名

爬虫异步化

pip install aiohttp								--支持异步发送网络请求的模块

案例1

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 = [
            '...',
            '...',
            '...'
        ]
        tasks = [asyncio.create_task(fetch(session,url)) for url in url_list]
        done,pending = await asyncio.wait(tasks)
        print(done,pending)
if __name__ == '__main__':
    asyncio.run(main())

总结:

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

像风一样的男人@

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值