在本文中,我们展示了如何在Python中生成并发HTTP请求。
超文本传输协议(HTTP)是分布式协作超媒体信息系统的应用协议。在HTTP协议中,客户端和服务器通过交换消息进行通信。客户端(通常是Web浏览器)发送的消息称为请求,而服务器发送的作为应答的消息称为响应。
并发请求
请求可以按顺序或并发处理。顺序请求被一一管理。如果我们处理许多请求,这可能会很低效。在并发请求中,程序不会等待一个请求完成来处理另一个请求;它们是同时处理的。
有两种生成并发HTTP请求的基本方法:通过多线程或通过异步编程。在多线程方法中,每个请求都由特定线程处理。在异步编程中,(通常)有一个线程和一个事件循环,它定期检查任务是否完成。
在Python中,我们可以使用ThreadPoolExecutor来产生并发请求。要做异步编程,我们可以使用asyncio模块。有了该模块,我们还需要使用支持异步编程的模块,比如aiohttp或者httpx.
Python同步HTTP请求
在第一个示例中,我们同步创建多个HTTP请求。为了测量经过的时间,我们使用perf_counter函数。
#!/usr/bin/python
import requests as req
import time
urls = ['http://webcode.me', 'https://httpbin.org/get',
'https://google.com', 'https://stackoverflow.com',
'https://github.com', 'https://clojure.org',
'https://fsharp.org']
tm1 = time.perf_counter()
for url in urls:
resp = req.get(url)
print(resp.status_code)
tm2 = time.perf_counter()
print(f'Total time elapsed: {tm2-tm1:0.2f} seconds')
在示例中,我们向七个网站生成HTTP请求并检索它们的状态代码。我们使用requests库。请求同步执行,一个接一个。
$ ./mul_sync.py 200 200 200 200 200 200 200 Total time elapsed: 2.96 seconds
Python异步HTTP请求
以下示例生成异步HTTP请求。
#!/usr/bin/python
import httpx
import asyncio
import time
async def get_async(url):
async with httpx.AsyncClient() as client:
return await client.get(url)
urls = ['http://webcode.me', 'https://httpbin.org/get',
'https://google.com', 'https://stackoverflow.com',
'https://github.com', 'https://clojure.org',
'https://fsharp.org']
async def launch():
resps = await asyncio.gather(*map(get_async, urls))
data = [resp.status_code for resp in resps]
for status_code in data:
print(status_code)
tm1 = time.perf_counter()
asyncio.run(launch())
tm2 = time.perf_counter()
print(f'Total time elapsed: {tm2-tm1:0.2f} seconds')
该示例使用httpx模块创建异步客户端,使用asyncio模块创建事件循环并安排异步任务。
async def get_async(url):
async with httpx.AsyncClient() as client:
return await client.get(url)
在Python异步编程中,我们使用协程。协程用async关键字修饰。await关键字用于等待协程并在函数完成后获取其结果。
resps = await asyncio.gather(*map(get_async, urls))
使用asyncio.gather函数同时处理多个协程。
$ ./mul_async.py 200 200 301 200 200 200 200 Total time elapsed: 0.93 seconds
Python多线程HTTP请求
在下一个示例中,我们使用ThreadPoolExecutor生成并发HTTP请求。ThreadPoolExecutor使用线程池来并发执行调用。
#!/usr/bin/python
import requests
import concurrent.futures
import time
def get_status(url):
resp = requests.get(url=url)
return resp.status_code
urls = ['http://webcode.me', 'https://httpbin.org/get',
'https://google.com', 'https://stackoverflow.com',
'https://github.com', 'https://clojure.org',
'https://fsharp.org']
tm1 = time.perf_counter()
with concurrent.futures.ThreadPoolExecutor() as executor:
futures = []
for url in urls:
futures.append(executor.submit(get_status, url=url))
for future in concurrent.futures.as_completed(futures):
print(future.result())
tm2 = time.perf_counter()
print(f'Total time elapsed: {tm2-tm1:0.2f} seconds')
ThreadPoolExecutor位于concurrent.futures模块中。
with concurrent.futures.ThreadPoolExecutor() as executor:
创建了一个ThreadPoolExecutor。
futures.append(executor.submit(get_status, url=url))
submit方法调度函数并返回表示函数执行的futureobject。
for future in concurrent.futures.as_completed(futures):
print(future.result())
as_completed方法返回一个对未来的迭代器。它会在它们完成时产生期货。
$ ./threaded.py 200 200 200 200 200 200 200 Total time elapsed: 0.86 seconds
在本文中,我们展示了如何在Python中生成并发HTTP请求。
列出所有Python教程。
