当多线程或者多进程足够多时,实际上并不能解决性能的瓶颈问题,也就是多线程和多进程对小规模的请求可以提高效率,过多的请求实际上会降低服务资源响应效率,因此协程是更好的解决文案。
当一个程序遇到阻塞时,如果将这个程序挂起,然后将它的cpu权限拿出来去执行我们的其他程序,执行完后再回过头来执行这些挂起的程序,此时所有非阻塞操作已经执行完毕,最后在一起执行阻塞程序,是不是相当于做了异步。
因此,协程的作用就是检测阻塞的程序,在单进程和单线程的情况下实现异步,相比多线程和多进程效率更高。
特殊函数
协程:
任务:
事件循环:
import asyncio
import time
# 创建特殊函数,一般将有阻塞操作设置特殊函数,在普通函数前加关键字async
async def get_request(url):
print('正在请求的网址是:',url)
time.sleep(2)
print('请求网址结束!')
return 123
# 自定义一个回调函数(一般来做数据解析),给任务对象使用:必须有一个参数,用来获取特殊函数的返回值
def t_callback(t):
#参数t就是任务对象
data = t.result() #result()函数就可以返回特殊函数内部的返回值
print('获取到特殊函数的返回值为:',data)
# 创建协程对象
c = get_request('www.123.com')
# 创建任务对象
task = asyncio.ensure_future(c)
# 任务对象添加回调函数,事件对象启用的时候,特殊函数和回调函数按顺序执行
task.add_done_callback(t_callback)
#创建事件循环对象
loop = asyncio.get_event_loop()
loop.run_until_complete(task)
特殊函数内部,不可以出现不支持异步模块的代码,否则会中断整个异步效果,例如sleep,requests,可以通过执行程序来判断。具体操作如下:
添加多个任务需要使用:wait()函数,其他代码省略,看最后一步
'''
特殊函数
回调函数
协程对象
'''
tasks = []
for url in urls:
c = get_request(url)
task = asyncio.ensure_future(c)
task.add_done_callback(parse)
tasks.append(task)
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))
await关键字:挂起发生阻塞操作的任务对象。在任务对象表示的操作中,凡是阻塞操作的前面都必须加上await关键字进行修饰,但不是所有阻塞操作都可以加await,需要添加支持协程的阻塞操作,await才会生效
async def get_request(url):
print('正在请求:',url)
await asyncio.sleep(2)
print('请求结束:',url)
# 特殊函数先写出基本的网络请求框架,然后在每个with前面加async,每个阻塞操作前await,便于看懂和记忆
# 使用with是为了关闭协程,避免浪费资源
import asyncio
import time
from lxml import etree
import aiohttp
start = time.time()
urls = [
'https://www.baidu.com',
'https://www.baidu.com',
'https://www.baidu.com'
]
#该任务是用来对指定url发起请求,获取响应数据
async def get_request(url):
# requests是不支持异步的模块,所以加了await也没用
# response = await requests.get(url=url)
#aiohttp是支持协程的网络请求,跟requests类似,创建请求对象(aiohttp_requests)
async with aiohttp.ClientSession() as aiohttp_requests:
#get请求,常用参数:url,headers,params,proxy
#post请求,常用参数:url,headers,data,proxy
#aiohttp处理代理的参数和requests不一样(注意),此处处理代理使用proxy='http://ip:port'
async with await aiohttp_requests.get(url=url) as response:
page_text = await response.text()
#text():获取字符串形式的响应数据
#read():获取二进制形式的响应数据
await asyncio.sleep(2)
return page_text
def call_back(t):#回调函数专门用于数据解析
#获取任务对象请求到的页面源码数据
page_text = t.result()
tree = etree.HTML(page_text)
a = tree.xpath('//a[1]/@href')
print(a)
tasks = []
for url in urls:
c = get_request(url)
task = asyncio.ensure_future(c)
task.add_done_callback(call_back)
tasks.append(task)
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))
print('总耗时:',time.time()-start)
uvloop基于libuv,libuv是一个使用C语言实现的高性能异步I/O库,uvloop用来代替asyncio默认事件循环,可以进一步加快异步I/O操作的速度。
import uvloop
loop = asyncio.get_event_loop()
asyncio.set_event_loop_policy(uvloop.EventLoopPolicy()) # 在启用事件前加这一行代码即可
loop.run_until_complete(asyncio.wait(tasks))