Python异步编程与事件循环

异步编程概述

异步编程是一种编程范式,允许程序在等待某些操作(如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 generatoryield语句生成异步数据流

异步上下文管理器模式:通过实现__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()")

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值