You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Tornado IOLoop性能分析:统计指定请求处理器的await执行次数

统计异步请求处理器交还IOLoop的次数

下面提供几种适配asyncio/Tornado的实现方案,可覆盖自研及第三方嵌套协程的统计需求:

方案1:全局Task跟踪(覆盖所有协程)

通过修改asyncio的Task工厂,注入统计逻辑,每次协程因await交还控制权时计数:

import asyncio
from tornado.web import RequestHandler, Application

# 全局计数变量,每个请求开始时重置
request_yield_count = 0

def counting_task_factory(loop, coro):
    async def coro_wrapper():
        nonlocal request_yield_count
        try:
            while True:
                # 每次await触发协程暂停,计数+1
                request_yield_count += 1
                result = await coro.__anext__()
                yield result
        except StopAsyncIteration:
            pass
    return asyncio.Task(coro_wrapper(), loop=loop)

class StatsHandler(RequestHandler):
    async def get(self):
        global request_yield_count
        request_yield_count = 0  # 重置请求专属计数
        
        # 模拟业务逻辑:嵌套自研协程+第三方协程
        await self.process_data()
        await self.call_third_party_api()
        
        self.write(f"本次请求交还IOLoop次数:{request_yield_count}")
    
    async def process_data(self):
        await asyncio.sleep(0.05)
        await self.parse_detail()
    
    async def parse_detail(self):
        await asyncio.sleep(0.02)
    
    async def call_third_party_api(self):
        # 模拟第三方协程调用
        await asyncio.sleep(0.03)

if __name__ == "__main__":
    loop = asyncio.get_event_loop()
    loop.set_task_factory(counting_task_factory)
    app = Application([(r"/stats", StatsHandler)])
    app.listen(8888)
    loop.run_forever()

说明:该方案会统计所有Task的await触发的控制权交还,包括框架内部、自研及第三方协程。若需仅统计当前请求相关的协程,可在请求开始时记录当前Task ID,在计数逻辑中过滤非当前请求的Task。

方案2:协程装饰器(灵活统计指定协程)

如果不需要全局统计,可给目标协程添加装饰器,针对性跟踪其内部的控制权交还次数:

import asyncio
from tornado.web import RequestHandler, Application

def count_yields(func):
    async def wrapper(*args, **kwargs):
        yield_count = 0
        coro_iter = func(*args, **kwargs)
        try:
            while True:
                yield_count += 1
                result = await coro_iter.__anext__()
                yield result
        except StopAsyncIteration:
            pass
        print(f"协程 {func.__name__} 交还控制权 {yield_count} 次")
        return yield_count
    return wrapper

class StatsHandler(RequestHandler):
    async def get(self):
        total_count = 0
        
        # 统计自研嵌套协程
        total_count += await self.process_data()
        # 统计第三方协程(通过包装实现)
        total_count += await self.wrap_third_party()
        
        self.write(f"本次请求总交还次数:{total_count}")
    
    @count_yields
    async def process_data(self):
        await asyncio.sleep(0.05)
        await self.parse_detail()
    
    @count_yields
    async def parse_detail(self):
        await asyncio.sleep(0.02)
    
    async def wrap_third_party(self):
        # 包装第三方协程以统计
        @count_yields
        async def wrapped_api_call():
            await asyncio.sleep(0.03)  # 模拟第三方协程
        return await wrapped_api_call()

if __name__ == "__main__":
    app = Application([(r"/stats", StatsHandler)])
    app.listen(8888)
    asyncio.get_event_loop().run_forever()

说明:该方案灵活性高,可按需选择要统计的协程,但需要手动包裹所有目标协程(包括第三方的)。

注意事项

  • 统计的是每次await导致的协程暂停,即交还执行权给IOLoop的次数,与需求完全匹配。
  • 生产环境使用时需注意性能开销,调试完成后建议移除或关闭统计逻辑。
  • 若需区分不同类型的交还(如IO等待、CPU密集型暂停),可在计数逻辑中结合asyncio的事件钩子做进一步过滤。

内容的提问来源于stack exchange,提问作者Eric

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 11:22:18