异步编程概述
异步编程是一种编程范式,允许程序在等待某些操作(如I/O操作)完成时继续执行其他任务,而不是阻塞等待。
import asyncio
import time
import requests
import aiohttp
# 同步版本 - 阻塞式
def sync_fetch_url(url):
"""同步获取URL内容"""
print(f"开始获取: {url}")
response = requests.get(url)
print(f"完成获取: {url}, 状态码: {response.status_code}")
return response.text
def sync_main():
"""同步主函数"""
start_time = time.time()
urls = [
'https://httpbin.org/delay/1',
'https://httpbin.org/delay/2',
'https://httpbin.org/delay/1'
]
for url in urls:
sync_fetch_url(url)
print(f"同步执行总时间: {time.time() - start_time:.2f}秒")
# 异步版本 - 非阻塞式
async def async_fetch_url(session, url):
"""异步获取URL内容"""
print(f"开始获取: {url}")
async with session.get(url) as response:
content = await response.text()
print(f"完成获取: {url}, 状态码: {response.status}")
return content
async def async_main():
"""异步主函数"""
start_time = time.time()
urls = [
'https://httpbin.org/delay/1',
'https://httpbin.org/delay/2',
'https://httpbin.org/delay/1'
]
async with aiohttp.ClientSession() as session:
tasks = [async_fetch_url(session, url) for url in urls]
await asyncio.gather(*tasks)
print(f"异步执行总时间: {time.time() - start_time:.2f}秒")
# 比较同步和异步性能
def compare_performance():
"""比较同步和异步性能"""
print("=== 同步执行 ===")
sync_main()
print("\n=== 异步执行 ===")
asyncio.run(async_main())
if __name__ == "__main__":
compare_performance()
asyncio事件循环机制
事件循环基础
事件循环是一个无限循环的程序结构,它负责:调度和执行异步任务(协程)、管理I/O事件(如网络、文件操作)、处理定时器和回调函数。
简单来说,事件循环就像一个“任务调度中心”,它不断检查是否有任务准备好执行,然后依次运行它们,直到所有任务完成。
import asyncio
import threading
class EventLoopExplorer:
"""事件循环探索器"""
@staticmethod
def basic_event_loop():
print("=== 基础事件循环 ===")
async def hello():
print("Hello")
await asyncio.sleep(1)
print("World")
# 方式1: asyncio.run() - 推荐方式
print("使用 asyncio.run():")
asyncio.run(hello())
# 方式2: 手动管理事件循环
print("\n手动管理事件循环:")
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
try:
loop.run_until_complete(hello())
finally:
loop.close()
@staticmethod
async def explore_loop_methods():
"""探索事件循环方法"""
print("\n=== 事件循环方法探索 ===")
loop = asyncio.get_running_loop()
print(f"当前循环: {loop}")
print(f"循环是否运行中: {loop.is_running()}")
print(f"循环线程ID: {threading.current_thread().ident}")
# 调度回调
def callback(name):
print(f"回调函数 {name} 被执行")
# call_soon - 尽快执行
loop.call_soon(callback, "call_soon")
# call_later - 延迟执行
loop.call_later(0.5, callback, "call_later")
# call_at - 在指定时间执行
loop.call_at(loop.time() + 1, callback, "call_at")
await asyncio.sleep(2) # 等待回调执行
@staticmethod
async def task_vs_coroutine():
"""任务 vs 协程"""
print("\n=== Task vs Coroutine ===")
async def sample_coro(name, delay):
print(f"协程 {name} 开始")
await asyncio.sleep(delay)
print(f"协程 {name} 完成")
return f"Result from {name}"
# 直接等待协程
print("直接等待协程:")
result = await sample_coro("direct", 1)
print(f"结果: {result}")
# 创建任务
print("\n创建并管理任务:")
task1 = asyncio.create_task(sample_coro("task1", 1))
task2 = asyncio.create_task(sample_coro("task2", 0.5))
print(f"Task1 状态: {task1.done()}")
print(f"Task2 状态: {task2.done()}")
# 等待任务完成
results = await asyncio.gather(task1, task2)
print(f"任务结果: {results}")
print(f"Task1 状态: {task1.done()}")
print(f"Task2 状态: {task2.done()}")
# 运行事件循环探索
def explore_event_loop():
explorer = EventLoopExplorer()
explorer.basic_event_loop()
asyncio.run(explorer.explore_loop_methods())
asyncio.run(explorer.task_vs_coroutine())
explore_event_loop()
事件循环的生命周期
| 生命周期阶段 | 说明 |
|---|---|
| 初始化 | 创建事件循环,配置异常处理和信号监听。 |
| 运行中 | 事件循环调度异步任务,处理I/O和回调。 |
| 信号捕获 | 监听系统信号,触发关闭事件。 |
| 优雅关闭 | 取消任务,执行清理,等待任务完成。 |
| 退出 | 关闭事件循环,释放资源,程序终止。 |
异步编程模式与最佳实践
常用异步编程模式
生产者-消费者模式:使用异步队列协调生产者和消费者之间的数据流
异步迭代器模式:通过实现__aiter__和__anext__方法创建异步可迭代对象
异步生成器模式:使用async generator和yield语句生成异步数据流
异步上下文管理器模式:通过实现__aenter__和__aexit__方法管理异步资源
异步库的选择与使用
| 异步库 | 功能范围 | 主要特点 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|---|
| asyncio | 标准库,事件循环 | Python内置,支持协程、任务调度 | 标准库,无需额外安装;生态丰富 | API较底层,使用复杂;学习曲线较陡 | 异步编程基础;自定义异步框架 |
| aiohttp | 异步HTTP客户端/服务 | 支持HTTP客户端和服务器 | 功能完善,社区活跃;支持WebSocket | 仅限HTTP协议;性能一般 | 异步HTTP请求、Web服务开发 |
| httpx | 异步HTTP客户端 | 支持同步和异步API,兼容requests风格 | API友好,支持HTTP/2;易用性好 | 服务器端支持有限 | 异步HTTP请求,替代requests |
| aiofiles | 异步文件I/O | 异步读写文件,基于asyncio | 简单易用,支持文件异步操作 | 仅限文件I/O | 异步文件读写 |
| aiodns | 异步DNS解析 | 基于c-ares库,异步DNS查询 | 高效DNS解析,非阻塞 | 依赖c-ares,安装复杂 | 异步DNS解析 |
| trio | 异步框架 | 结构化并发,简洁安全 | API设计优雅,错误处理友好 | 生态较小,兼容性问题 | 需要高安全性和简洁性的异步应用 |
| curio | 异步框架 | 轻量级,基于协程 | 简单易用,性能优良 | 生态有限,社区小 | 学习异步编程,实验性项目 |
| Twisted | 异步网络框架 | 支持多协议,成熟稳定 | 功能强大,支持多种协议 | API复杂,学习曲线陡峭 | 复杂网络应用,协议开发 |
| Tornado | 异步Web框架 | 高性能Web服务器,支持异步 | 轻量级,集成HTTP服务器 | 生态不如Django/Flask丰富 | 高性能Web服务,长连接应用 |
实战项目:高性能Web服务器
基础Web服务器框架
import asyncio
import json
import urllib.parse
from typing import Dict, Any, Callable, Optional, Tuple
from dataclasses import dataclass, asdict
from datetime import datetime
import logging
@dataclass
class HTTPRequest:
method: str
path: str
headers: Dict[str, str]
body: str
query_params: Dict[str, str]
@dataclass
class HTTPResponse:
status_code: int = 200
headers: Dict[str, str] = None
body: str = ""
def __post_init__(self):
if self.headers is None:
self.headers = {}
# 设置默认headers
if 'Content-Type' not in self.headers:
self.headers['Content-Type'] = 'text/plain'
if 'Server' not in self.headers:
self.headers['Server'] = 'AsyncPythonServer/1.0'
class AsyncWebServer:
"""高性能异步Web服务器"""
def __init__(self, host: str = 'localhost', port: int = 8000):
self.host = host
self.port = port
self.routes: Dict[Tuple[str, str], Callable] = {}
self.middleware: List[Callable] = []
self.server = None
def route(self, path: str, method: str = 'GET'):
"""路由装饰器"""
def decorator(func: Callable):
self.routes[(method.upper(), path)] = func
return func
return decorator
def middleware_decorator(self, func: Callable):
"""中间件装饰器"""
self.middleware.append(func)
return func
def parse_request(self, data: str) -> HTTPRequest:
"""解析HTTP请求"""
lines = data.strip().split('\r\n')
# 解析请求行
request_line = lines[0]
method, full_path, version = request_line.split()
# 解析路径和查询参数
if '?' in full_path:
path, query_string = full_path.split('?', 1)
query_params = dict(urllib.parse.parse_qsl(query_string))
else:
path = full_path
query_params = {}
# 解析headers
headers = {}
body_start = 1
for i, line in enumerate(lines[1:], 1):
if line == '':
body_start = i + 1
break
key, value = line.split(':', 1)
headers[key.strip()] = value.strip()
# 解析body
body = '\r\n'.join(lines[body_start:]) if body_start < len(lines) else ''
return HTTPRequest(method, path, headers, body, query_params)
def format_response(self, response: HTTPResponse) -> str:
"""格式化HTTP响应"""
status_line = f"HTTP/1.1 {response.status_code} OK\r\n"
# 添加Content-Length
response.headers['Content-Length'] = str(len(response.body.encode()))
response.headers['Connection'] = 'close'
headers_str = ''.join(f"{k}: {v}\r\n" for k, v in response.headers.items())
return f"{status_line}{headers_str}\r\n{response.body}"
async def handle_request(self, request: HTTPRequest) -> HTTPResponse:
"""处理HTTP请求"""
# 应用中间件
for middleware in self.middleware:
await middleware(request)
# 路由匹配
route_key = (request.method, request.path)
if route_key in self.routes:
handler = self.routes[route_key]
try:
return await handler(request)
except Exception as e:
logging.error(f"处理请求时出错: {e}")
return HTTPResponse(
status_code=500,
body=f"Internal Server Error: {str(e)}"
)
else:
return HTTPResponse(
status_code=404,
body="Not Found"
)
async def handle_client(self, reader: asyncio.StreamReader, writer: asyncio.StreamWriter):
"""处理客户端连接"""
client_addr = writer.get_extra_info('peername')
logging.info(f"客户端连接: {client_addr}")
try:
# 读取请求数据
data = await reader.read(4096)
request_str = data.decode('utf-8')
if not request_str:
return
# 解析请求
request = self.parse_request(request_str)
logging.info(f"{request.method} {request.path}")
# 处理请求
response = await self.handle_request(request)
# 发送响应
response_str = self.format_response(response)
writer.write(response_str.encode('utf-8'))
await writer.drain()
except Exception as e:
logging.error(f"处理客户端连接时出错: {e}")
error_response = HTTPResponse(status_code=500, body="Server Error")
response_str = self.format_response(error_response)
writer.write(response_str.encode('utf-8'))
await writer.drain()
finally:
writer.close()
await writer.wait_closed()
async def start_server(self):
"""启动服务器"""
self.server = await asyncio.start_server(
self.handle_client,
self.host,
self.port
)
addr = self.server.sockets[0].getsockname()
logging.info(f"服务器启动在 http://{addr[0]}:{addr[1]}")
async with self.server:
await self.server.serve_forever()
def run(self):
"""运行服务器"""
logging.basicConfig(level=logging.INFO)
try:
asyncio.run(self.start_server())
except KeyboardInterrupt:
logging.info("服务器停止")
# 创建Web服务器实例
app = AsyncWebServer()
# 示例中间件
@app.middleware_decorator
async def logging_middleware(request: HTTPRequest):
"""日志中间件"""
timestamp = datetime.now().isoformat()
logging.info(f"[{timestamp}] {request.method} {request.path}")
@app.middleware_decorator
async def cors_middleware(request: HTTPRequest):
"""CORS中间件"""
# 在实际响应中添加CORS头部
pass
# 示例路由
@app.route('/')
async def home(request: HTTPRequest) -> HTTPResponse:
"""首页"""
return HTTPResponse(
headers={'Content-Type': 'text/html'},
body="""
<html>
<head><title>异步Python服务器</title></head>
<body>
<h1>欢迎使用异步Python服务器!</h1>
<p>当前时间: {}</p>
<a href="/api/users">用户API</a>
</body>
</html>
""".format(datetime.now().isoformat())
)
@app.route('/api/users')
async def get_users(request: HTTPRequest) -> HTTPResponse:
"""获取用户列表API"""
# 模拟数据库查询
await asyncio.sleep(0.1)
users = [
{"id": 1, "name": "张三", "email": "zhangsan@example.com"},
{"id": 2, "name": "李四", "email": "lisi@example.com"}
]
return HTTPResponse(
headers={'Content-Type': 'application/json'},
body=json.dumps(users, ensure_ascii=False)
)
@app.route('/api/users', 'POST')
async def create_user(request: HTTPRequest) -> HTTPResponse:
"""创建用户API"""
try:
user_data = json.loads(request.body)
# 模拟数据库插入
await asyncio.sleep(0.1)
# 返回创建的用户信息
new_user = {
"id": 3,
"name": user_data.get("name"),
"email": user_data.get("email"),
"created_at": datetime.now().isoformat()
}
return HTTPResponse(
status_code=201,
headers={'Content-Type': 'application/json'},
body=json.dumps(new_user, ensure_ascii=False)
)
except json.JSONDecodeError:
return HTTPResponse(
status_code=400,
body="Invalid JSON"
)
# 启动服务器(注释掉避免在导入时自动运行)
# if __name__ == '__main__':
# app.run()
增强版Web服务器
import asyncio
import json
import ssl
from typing import Dict, Any, List, Optional
from pathlib import Path
import mimetypes
from urllib.parse import unquote
class EnhancedAsyncWebServer(AsyncWebServer):
"""增强版异步Web服务器"""
def __init__(self, host: str = 'localhost', port: int = 8000,
static_dir: str = None, ssl_context: ssl.SSLContext = None):
super().__init__(host, port)
self.static_dir = Path(static_dir) if static_dir else None
self.ssl_context = ssl_context
self.connection_pool: Dict[str, int] = {}
self.max_connections_per_ip = 10
async def handle_client(self, reader: asyncio.StreamReader, writer: asyncio.StreamWriter):
"""增强的客户端处理"""
client_addr = writer.get_extra_info('peername')
client_ip = client_addr[0] if client_addr else 'unknown'
# 连接数限制
if self.connection_pool.get(client_ip, 0) >= self.max_connections_per_ip:
writer.write(b"HTTP/1.1 429 Too Many Requests\r\n\r\n")
await writer.drain()
writer.close()
return
self.connection_pool[client_ip] = self.connection_pool.get(client_ip, 0) + 1
try:
await super().handle_client(reader, writer)
finally:
self.connection_pool[client_ip] -= 1
if self.connection_pool[client_ip] <= 0:
del self.connection_pool[client_ip]
async def serve_static_file(self, file_path: str) -> HTTPResponse:
"""提供静态文件服务"""
if not self.static_dir:
return HTTPResponse(status_code=404, body="Static files not configured")
# 安全检查:防止路径遍历攻击
requested_path = self.static_dir / unquote(file_path.lstrip('/'))
try:
requested_path = requested_path.resolve()
if not str(requested_path).startswith(str(self.static_dir.resolve())):
return HTTPResponse(status_code=403, body="Forbidden")
except (OSError, ValueError):
return HTTPResponse(status_code=400, body="Bad Request")
if not requested_path.exists():
return HTTPResponse(status_code=404, body="File not found")
if not requested_path.is_file():
return HTTPResponse(status_code=403, body="Forbidden")
# 读取文件
try:
with open(requested_path, 'rb') as f:
content = f.read()
# 确定MIME类型
mime_type, _ = mimetypes.guess_type(str(requested_path))
if mime_type is None:
mime_type = 'application/octet-stream'
return HTTPResponse(
headers={'Content-Type': mime_type},
body=content.decode('utf-8') if mime_type.startswith('text/') else content
)
except (IOError, UnicodeDecodeError) as e:
return HTTPResponse(status_code=500, body=f"Error reading file: {e}")
async def handle_request(self, request: HTTPRequest) -> HTTPResponse:
"""增强的请求处理"""
# 首先检查是否是静态文件请求
if self.static_dir and request.path.startswith('/static/'):
return await self.serve_static_file(request.path[8:]) # 移除 '/static/' 前缀
# 否则使用原来的路由处理
return await super().handle_request(request)
async def start_server(self):
"""启动增强服务器"""
self.server = await asyncio.start_server(
self.handle_client,
self.host,
self.port,
ssl=self.ssl_context
)
addr = self.server.sockets[0].getsockname()
protocol = "https" if self.ssl_context else "http"
logging.info(f"增强服务器启动在 {protocol}://{addr[0]}:{addr[1]}")
async with self.server:
await self.server.serve_forever()
# WebSocket支持
class WebSocketConnection:
"""WebSocket连接处理"""
def __init__(self, reader: asyncio.StreamReader, writer: asyncio.StreamWriter):
self.reader = reader
self.writer = writer
self.closed = False
async def send(self, message: str):
"""发送WebSocket消息"""
if self.closed:
return
# 简化的WebSocket帧格式(实际实现会更复杂)
frame = f"\x81{chr(len(message))}{message}".encode()
self.writer.write(frame)
await self.writer.drain()
async def receive(self) -> Optional[str]:
"""接收WebSocket消息"""
if self.closed:
return None
try:
# 简化的WebSocket帧解析
data = await self.reader.read(1024)
if not data:
self.closed = True
return None
# 实际实现需要完整的WebSocket协议解析
return data.decode('utf-8')
except Exception:
self.closed = True
return None
async def close(self):
"""关闭WebSocket连接"""
if not self.closed:
self.closed = True
self.writer.close()
await self.writer.wait_closed()
# 性能监控
class PerformanceMonitor:
"""性能监控"""
def __init__(self):
self.request_count = 0
self.total_response_time = 0
self.start_time = time.time()
self.active_connections = 0
def record_request(self, response_time: float):
"""记录请求"""
self.request_count += 1
self.total_response_time += response_time
def get_stats(self) -> Dict[str, Any]:
"""获取统计信息"""
uptime = time.time() - self.start_time
avg_response_time = (self.total_response_time / self.request_count
if self.request_count > 0 else 0)
return {
"uptime": uptime,
"total_requests": self.request_count,
"avg_response_time": avg_response_time,
"requests_per_second": self.request_count / uptime if uptime > 0 else 0,
"active_connections": self.active_connections
}
# 创建增强版服务器实例
enhanced_app = EnhancedAsyncWebServer(static_dir="static")
monitor = PerformanceMonitor()
# 性能监控中间件
@enhanced_app.middleware_decorator
async def performance_monitoring_middleware(request: HTTPRequest):
"""性能监控中间件"""
request._start_time = time.time()
# 监控端点
@enhanced_app.route('/api/stats')
async def get_stats(request: HTTPRequest) -> HTTPResponse:
"""获取服务器统计信息"""
stats = monitor.get_stats()
return HTTPResponse(
headers={'Content-Type': 'application/json'},
body=json.dumps(stats, indent=2)
)
# 健康检查端点
@enhanced_app.route('/health')
async def health_check(request: HTTPRequest) -> HTTPResponse:
"""健康检查"""
return HTTPResponse(
headers={'Content-Type': 'application/json'},
body=json.dumps({"status": "healthy", "timestamp": datetime.now().isoformat()})
)
# 测试端点
@enhanced_app.route('/api/echo', 'POST')
async def echo(request: HTTPRequest) -> HTTPResponse:
"""回显测试"""
return HTTPResponse(
headers={'Content-Type': 'application/json'},
body=json.dumps({
"method": request.method,
"path": request.path,
"headers": dict(request.headers),
"body": request.body,
"query_params": request.query_params
}, ensure_ascii=False, indent=2)
)
print("Web服务器代码已定义完成。")
print("要启动服务器,请运行: enhanced_app.run()")

1516

被折叠的 条评论
为什么被折叠?



