Tornado默认不复用连接是因为AsyncHTTPClient默认使用SimpleAsyncHTTPClient,它“一次一连”、无连接池;必须显式配置CurlAsyncHTTPClient、force_instance=False,并添加Connection: keep-alive请求头及服务端响应支持,才能实现长连接复用。

tornado 本身就是为了长连接而生的,不是“支持”长连接,而是默认就靠它活命——用错模式(比如强行套 WSGI)或配错客户端行为,反而会让长连接失效。
为什么 AsyncHTTPClient 默认不复用连接?
你写 await http_client.fetch(...),但每次请求都新建 TCP 连接?大概率是没配对 CurlAsyncHTTPClient 或漏了 Connection: keep-alive。默认的 SimpleAsyncHTTPClient 是“一次一连”,不维护连接池。
- 必须显式配置:
tornado.httpclient.AsyncHTTPClient.configure('tornado.curl_httpclient.CurlAsyncHTTPClient') -
force_instance=False(关键!否则每个 handler 实例都新建 client,连接池失效) - 加请求头:
headers={'Connection': 'keep-alive'} - 服务端也要响应
Connection: keep-alive,否则对方主动断连
WebSocketHandler 连接断开却不触发 on_close?
常见于 Nginx 反向代理未透传 WebSocket 协议头,或客户端静默掉线后服务端未及时感知。Tornado 不会主动心跳,得自己补。
- Nginx 配置必须含:
proxy_http_version 1.1;、proxy_set_header Upgrade $http_upgrade;、proxy_set_header Connection "upgrade"; - 在
WebSocketHandler中启动定时心跳:self.ping(),并重载on_pong做存活标记 -
self.close()不等于立刻断开,要等on_close回调才真正清理资源;别在on_message里直接删self引用
并发上万连接时 CPU 暴涨,但 IOLoop 没卡住?
说明瓶颈不在网络层,而在业务逻辑里混入了同步阻塞操作——比如用 requests.get、time.sleep、或没加 await 的数据库驱动。
快速生成专业的 Python 脚本和应用代码。一键创建完整项目结构,支持CLI、API、爬虫、Bot、Django等多种项目类型,包含完整的项目结构、配置文件、依赖管理、测试、README和文档。
- 所有 I/O 操作必须异步:数据库用
asyncpg/motor,HTTP 调用走AsyncHTTPClient,文件读写用asyncio.to_thread包装 - 避免在协程里做密集计算;CPU 密集型任务丢给
concurrent.futures.ProcessPoolExecutor -
tornado.ioloop.IOLoop.current().run_in_executor是唯一安全的线程/进程桥接方式
部署时要不要加 Nginx?
单机跑 tornado 完全可以直面公网,但它不处理 SSL 终止、静态文件缓存、连接限速这些事——这些不是它该干的活。
立即学习“Python免费学习笔记(深入)”;
- 生产环境建议前置 Nginx:卸载 HTTPS、防慢连接攻击、限制单 IP 并发数(
limit_conn)、缓存/static/ - 别用 Nginx +
WSGIContainer:那等于把tornado当成 Flask 用,WebSocket和长轮询全废 - 多进程部署时,每个 worker 独享一个
IOLoop,用bind_sockets+fork_processes,别手动os.fork
keep-alive 头,或漏一次 await,整条链就退化成短连接。


















