经典案例
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等待时间去做一些其他事情.