如果你最近在用 FastAPI或者任何一个基于 asyncio 的 Python Web 框架那你大概率在终端里敲过这样一行命令uvicorn main:app --reload。很多朋友把 Uvicorn 当成框架自带的小工具用它启动服务、调试接口然后就不再深究了。但 Uvicorn 并不是框架的附属品它是 Python 生态里专门用来运行异步 Web 应用的 ASGI 服务器是整个异步 Web 技术栈中最底层、也最关键的一道关口它负责接收浏览器或客户端的网络请求把 HTTP 消息、WebSocket 连接解析成标准化的 ASGI 事件流再交给 FastAPI、Starlette 这类异步框架去处理业务逻辑框架处理完它再把响应数据写回 socket。可以这么说框架是大脑Uvicorn 是手和嘴。今天这篇文章我打算把 Uvicorn 从里到外完整讲一遍——先讲它解决的协议问题再讲它为什么快然后是开发和生产环境下的完整配置清单接着是我实际部署中踩过的几个坑最后是它和其他 ASGI 服务器的选型边界。不管你是刚接触异步 Web 的新手还是已经部署过几个服务但总在配置上吃亏的开发者这篇应该都能帮上忙。1. Uvicorn 是什么异步 Web 请求的翻译官与看门人1.1 WSGI 时代的瓶颈一个请求一个线程的尴尬要理解 Uvicorn 存在的意义得先看一眼它替代的那个时代。Python Web 圈子里WSGIWeb Server Gateway Interface标准统治了非常多年Django、Flask 这些经典框架都是建立在 WSGI 之上的。WSGI 的工作方式很简单粗暴服务器收到一个 HTTP 请求就创建一个线程或者进程然后调用你写的应用函数。应用函数内部如果碰了数据库查询、第三方 HTTP 调用、读写文件那么这个线程就会卡在系统调用上什么都干不了只能等 IO 回来。问题是线程是有成本的。一个线程默认要占不少内存栈空间、线程控制块线程切换还要消耗 CPU 上下文切换时间。本地起个服务跑几千并发线程池直接打满后面来的请求只能排队。我见过不少 Django 项目在业务高峰时Gunicorn 的 worker 数顶到几十上百个内存吃紧CPU 大量花在线程切换上。这不是代码写得不高效而是同步模型的天花板就在那里它没法表达一个线程同时等好几个 IO哪个先到先处理哪个这种想法。asyncio 出现之后Python 终于有了统一的事件循环原语但 WSGI 规范本身根本不支持异步应用于是 ASGI 规范应运而生。1.2 ASGI 规范从函数调用到事件流的转变ASGI 的全称是 Asynchronous Server Gateway Interface可以理解为 WSGI 的异步接替者。它不再规定服务器调用一个普通函数这么简单而是重新定义了服务器和应用之间的通讯协议。ASGI 应用中每个连接到来时服务器会构造一个 scope 对象里面包含请求方法、路径、头信息等元数据应用是一个 async callable它接收(scope, receive, send)三个东西通过receive接收事件比如 HTTP 请求体、WebSocket 消息通过send发送事件比如 HTTP 响应头、响应体、WebSocket 消息。这个规范带来的好处首先是统一了 HTTP 和 WebSocket 的处理模型。WSGI 时代 WebSocket 基本是各玩各的因为 WSGI 的函数调用模型根本没法定住一条长连接。其次是引入了 lifespan 协议让应用在启动和关闭时可以执行初始化数据库连接池、清理资源这类生命周期代码。Uvicorn 就是这套规范的参考实现之一——它不写业务逻辑专注把 socket 字节流翻译成 ASGI 事件再把应用返回的事件写回 socket。1.3 一次请求从网卡到 FastAPI 的完整旅程我用一个最小例子串一遍。假设有个main.py里面是from fastapi import FastAPI app FastAPI() app.get(/ping) async def ping(): return {message: pong}命令行执行uvicorn main:app --reload拆开看这行命令main:app是模块名:应用实例名的定位方式Uvicorn 会去导入main模块拿到名为app的 ASGI 应用对象--reload是开发模式专用开启文件监听代码一改动就自动重启进程。用户浏览器发来一个GET /ping请求后链路是这样的Uvicorn 的监听 socket 先收到 TCP 连接接着它的 HTTP 解析器把请求头解析出来按照 ASGI 协议构造一个 scope 字典包括methodGET、path/ping这些字段。然后 Uvicornawait app(scope, receive, send)把控制权交给 FastAPI。FastAPI 的星型路由根据 path 匹配到ping函数执行完返回 JSON应用调用send把响应事件发出来。Uvicorn 拿到响应头、响应体后把它们编码成 HTTP 响应字节流写回 socket。整个过程里Uvicorn 和 FastAPI 之间唯一的边界就是你定义的app这个对象。1.4 Uvicorn 与 Starlette、FastAPI 的生态定位经常有人把 Uvicorn、Starlette、FastAPI 这三个词搞混其实关系特别清楚。用汽车打个比方Uvicorn 是发动机Starlette 是车架和底盘FastAPI 是在 Starlette 之上加了方向盘、仪表盘和智能驾驶系统的整车。FastAPI 依赖 StarletteStarlette 是一个 ASGI 框架但框架本身不能直接跑在网络上必须有一个 ASGI 服务器把它拉起来Uvicorn 就是这个发动机。你也可以写一个裸 ASGI 应用不引入任何框架然后用 Uvicorn 跑完全合法。选型的时候记住一条主线FastAPI/Starlette 官方文档默认推荐 Uvicorn因为它是这些框架作者 Tom Christie 本人维护的生态契合度最高。2. Uvicorn 的快从哪来uvloop、httptools 与协程模型2.1 线程换成事件循环等待不再占用劳力Uvicorn 性能好的根源首先在于它跑在事件循环模型上。传统同步服务器是每个连接一个线程连接多了就切换线程事件循环则是在一个线程里调度成千上万个协程谁在等 IO就被挂起让出执行权谁的数据到了就被唤醒继续跑。这个差异可以用餐厅来类比同步服务器的模式是每个客人配一个专属服务员服务员站在旁边等着客人吃完一道菜再服务下一道客人越多需要雇的服务员越多工资开销内存和互相走动避让上下文切换都很夸张。事件循环模式是一个服务员同时服务几十桌客人客人点了菜需要等待时服务员就离开去服务其他桌等菜做好了再回来端给对应的客人。所以单核 CPU 在事件循环模型下能同时 hold 住成千上万个空闲连接这在 IO 密集的 Web 场景里就是质变。2.2 uvloop把事件循环核心搬进 C 语言不过asyncio 自带的事件循环是纯 Python 实现的虽然已经能用但性能在追求极致的场景下还有提升空间。Uvicorn 的默认配置里只要平台支持会自动采用 uvloop 作为底层事件循环。uvloop 是 Cython 重写的事件循环实现底层复用 libuv——就是 Node.js 用的那套 IO 库——你可以把它理解成用 C 语言编写的事件循环内核。我在自己的项目里做过简单对比同一个 FastAPI 服务在纯 IO 接口压测下uvloop 模式比原生 asyncio 模式吞吐量能高出 2 到 3 倍。Uvicorn 提供了--loop参数可以手动指定用uvloop还是asyncio。需要注意的是uvloop 在 Windows 平台上不可用所以如果你在 Windows 上开发Uvicorn 会自动回退到 asyncio部署到 Linux 服务器后它又会自动切到 uvloop。这个自动切换的行为很贴心但也意味着你在 Windows 本地压出来的性能数据和线上 Linux 会有明显差异。2.3 httptoolsHTTP 解析器的性能要诀事件循环解决的是连接调度问题但还有一块很容易被忽略的瓶颈就是 HTTP 协议本身的解析。HTTP 请求头、请求体、分块传输编码这些都需要逐字节解析纯 Python 解析一帧数据要做的字节操作非常多。Uvicorn 默认使用 httptools 作为 HTTP 解析器。httptools 是从 Node.js 的 http-parser 移植过来的 C 扩展把解析请求头、解析分块编码这些高频动作全部下沉到了 C 层。Uvicorn 也支持--http h11切到纯 Python 实现的 h11。h11 的好处是代码完全可控、兼容性更好适合排查协议级问题但性能会打折。对于绝大多数线上服务保持默认的 httptools 就行。如果你在日志里发现 Uvicorn 打印了类似httptools 不可用回退到 h11的提示那就要注意一下是不是服务器环境缺了编译依赖导致 C 扩展没装成功。2.4 异步服务器的正确使用姿势别在协程里睡觉说了这么多性能优势有一个前提必须强调Uvicorn 快在 IO 并发而不是 CPU 并行。asyncio 是协作式调度事件循环只有在你await一个真正会挂起的异步操作时才会去调度其他协程。如果你在视图函数里写了同步阻塞代码比如requests.get()、time.sleep(2)那整个 worker 的事件循环就会卡住这个 worker 上所有的连接全部遭殃。最常见的错误就是把同步库直接搬进 FastAPI 里。我给你看一个反面例子import requests from fastapi import FastAPI app FastAPI() app.get(/bad) async def bad(): # requests.get 是同步阻塞调用会卡住整个事件循环 resp requests.get(https://api.example.com/data) return resp.json()正确的做法是使用异步库import httpx from fastapi import FastAPI app FastAPI() app.get(/good) async def good(): async with httpx.AsyncClient() as client: resp await client.get(https://api.example.com/data) return resp.json()这点直接决定了你用 Uvicorn 是体验到丝滑并发还是体验到并发一上去就超时。很多性能问题排查到最后根子不在服务器而在应用代码里那些拖住事件循环的同步调用。3. 从开发到生产Uvicorn 配置清单与推荐部署方案3.1 本地开发一条好用的调试命令开发阶段我的习惯命令是uvicorn main:app --reload --host 0.0.0.0 --port 8000--host 0.0.0.0是为了让局域网内其他设备比如手机、另一台电脑也能访问到本地服务方便联调--port 8000是 Uvicorn 默认端口如果被占了就换一个。--reload会启用一个独立的 reloader 进程监听当前目录下的文件变化代码一保存就自动重启服务省去手动重启的麻烦。它的底层依赖 watchfiles 这类监听库文件事件发生时会重新导入应用模块。需要特别注意的是--reload和--workers不能同时用。如果你在一个命令里既加了--reload又加了--workers 4Uvicorn 会打印一条警告然后把 worker 数强制设为 1。这条警告很容易被忽略但后果很隐蔽你以为自己在 4 进程的负载均衡下调试实际上只有一个 worker 在跑。开发模式下这问题不大但如果这条命令被原封不动抄到生产环境就有得好果子吃了这个坑我在下一节会细说。3.2 核心运行参数速查表用 Uvicorn 久了之后我发现真正需要熟练的参数其实就那么多。下面这张表是我自己整理的速查表覆盖了本地开发和线上部署最常用到的一批配置参数名默认值作用--host127.0.0.1绑定监听地址服务器部署一般设为 0.0.0.0--port8000监听端口--workers1worker 进程数多核场景按需增加--loopauto事件循环实现auto 在可用时自动切 uvloop--httpautoHTTP 解析器auto 默认选 httptools--wsautoWebSocket 实现auto 自动选 websockets / wsproto--env-file无启动时加载 .env 文件中的环境变量--log-levelinfo日志级别生产可调为 warning--access-log开启是否打印访问日志压测时建议关闭--proxy-headers开启是否信任 X-Forwarded-* 头部--forwarded-allow-ips127.0.0.1可以被信任的代理 IP 列表多个 IP 用逗号分隔--timeout-keep-alive5keep-alive 连接最长空闲时间秒--limit-concurrency无限制单 worker 最大并发连接数--limit-max-requests无限制worker 处理多少请求后自动重启能缓解内存泄漏--timeout-graceful-shutdown无限制优雅关停时最多等待多少秒其中--proxy-headers和--forwarded-allow-ips是一对容易踩坑的参数下章重点讲。3.3 生产部署方案一裸跑 Uvicorn 的推荐配置如果你在容器化环境里部署通常一个容器只跑一个 worker 进程交给 Kubernetes 这类平台去水平伸缩那直接裸跑 Uvicorn 是最简单的方案。我常用的生产命令长这样uvicorn main:app \ --host 0.0.0.0 \ --port 8000 \ --workers 2 \ --log-level warning \ --no-access-log \ --proxy-headers \ --forwarded-allow-ips 10.0.0.0/8,192.168.0.0/16 \ --timeout-keep-alive 5 \ --limit-concurrency 2048 \ --timeout-graceful-shutdown 30解释一下几个关键选择。--log-level warning是减少 info 级别日志刷屏但注意它不影响访问日志访问日志要单独用--no-access-log关掉。--forwarded-allow-ips必须按实际网络环境写如果你不知道 Nginx 具体 IP可以先写内网网段但生产环境建议精确到 IP。--limit-concurrency 2048是给单 worker 设一个并发上限防止慢客户端把进程资源吃满。--timeout-graceful-shutdown 30是给优雅退出留一个缓冲时间下一章详细讲为什么这个参数很重要。还有一个细节在 Docker 里如果 Uvicorn 是容器里的 1 号进程PID 1它会直接接收docker stop发出的 SIGTERM 信号然后执行优雅关闭。这一点 Uvicorn 处理得很好不需要额外包一个 tini。但如果你用了--workers多进程模式Uvicorn 的主进程会负责转发信号给 worker整体行为也还靠谱。3.4 生产部署方案二Gunicorn UvicornWorker 的经典组合另一种更经典的方案是用 Gunicorn 管理 worker 进程再让每个 worker 跑 Uvicorn 的 worker 类。命令是gunicorn -w 4 -k uvicorn.workers.UvicornWorker \ --bind 0.0.0.0:8000 \ --timeout 60 \ --graceful-timeout 30 \ main:app这里的核心是-k uvicorn.workers.UvicornWorkerGunicorn 作为进程管理器负责 worker 的创建、监控、重启每个 worker 内部通过 UvicornWorker 类跑完整的 ASGI 事件循环和高性能 HTTP 解析。这套组合的好处在于Gunicorn 的进程管理机制比 Uvicorn 自带的--workers更成熟尤其是在 worker 卡死自动重启、信号处理、平滑重载这些方面久经生产考验。什么时候选 Gunicorn 组合什么时候直接裸跑 Uvicorn我的经验是在虚机或裸机上跑传统部署用 Gunicorn 组合更稳在 K8s 容器里平台已经承担了进程调度和重启职责就裸跑 Uvicorn 更简单少一层依赖。两种方案没有高低之分只有合不合适。4. 部署踩坑实录代理头、reload、优雅退出与 WebSocket4.1 Nginx 反代后真实 IP 全变成 127.0.0.1最常见的第一个坑是 Nginx 反向代理 Uvicorn 之后应用日志和访问日志里的客户端 IP 全变成了127.0.0.1。原因很好理解浏览器先连 NginxNginx 再连 UvicornUvicorn 看到的 socket 连接来源自然就是 Nginx。解决办法是开--proxy-headers它会让 Uvicorn 信任 Nginx 传进来的X-Forwarded-For、X-Forwarded-Proto这些头部用里面的 IP 替代直连 IP。但这里有个安全细节--proxy-headers默认是开启的可 Uvicorn 默认只信任来自127.0.0.1和::1的代理。如果 Nginx 在另一台机器上你就必须通过--forwarded-allow-ips显式把 Nginx 所在 IP 或网段加进去否则开不开都一样真实 IP 依然拿不到。更需要注意的是如果你的服务直接暴露在外网没有任何前置代理千万不要开着--proxy-headers信任任意来源的X-Forwarded-For因为客户端可以自己伪造这个头把假 IP 写到你的日志里甚至影响一些基于 IP 的限流策略。这种伪造在日志分析时会非常误导人。所以先搞清楚你的网络拓扑里到底有几层代理再决定怎么配--forwarded-allow-ips。4.2 reload 命令误上生产之后第二个坑是开发时那条带--reload的命令被直接复制到生产环境。症状往往很诡异服务偶尔卡一下、代码文件一变更就神秘重启、压测时的 QPS 和 worker 数对不上。如果你在启动日志里看到类似于Started reloader process这样的字样那就说明 reload 模式已经启动了。reload 模式首先会多跑一个 reloader 父进程专门负责监听文件变化这本身就有额外的 CPU 和文件 IO 开销其次任何代码文件变动都会触发整个服务重启生产环境如果有多个人在服务器上动过代码服务就会莫名其妙地反复重启连接大量中断。我之前排查过一个案例对方的 Uvicorn 命令带了--reload又带了--workers 4日志里明明打了警告但由于部署脚本没有实时展示输出这个警告被完全淹没了结果是服务只有一个 worker流量高峰期性能很差。排查完把--reload去掉、重新以多 worker 启动问题立刻消失。我的建议是开发命令和生产命令写成两套独立的启动脚本或者用环境变量区分不要让--reload有机会混进生产环境的配置文件里。4.3 优雅关闭kill 之后请求没处理完就被切断第三个坑和请求的优雅退出有关。你在服务器上kill一个 Uvicorn 进程或者执行docker stop时如果处理得不好正在处理的请求会被直接切断用户会看到 502 或者连接被重置。Uvicorn 本身是支持优雅关闭的它收到 SIGINT 或 SIGTERM 后会停止接收新连接让正在处理中的请求走完然后退出。但有一个前提就是要给它足够的时间。默认情况下--timeout-graceful-shutdown是无限制等待这看起来很好但实际部署时很多编排系统会设定强杀时限。Kubernetes 默认在发送 SIGTERM 后等 30 秒超时就 SIGKILL如果你有超过 30 秒的长请求就会看到明明优雅退出了还是有请求被掐断的现象。解决方案是主动设置--timeout-graceful-shutdown 30之类的值并且让这个值略小于编排系统的强杀时限。如果你用 Gunicorn 管理则要关注--graceful-timeout参数它控制 worker 在收到退出信号后最多等待的秒数。生产环境我一般把 Uvicorn 的优雅退出时间设在 30 秒以内同时把 K8s 的 terminationGracePeriodSeconds 配到 45 秒以上留出余量。4.4 keep-alive 与并发限流慢客户端和连接复用怎么平衡第四个坑是和连接有关的权衡。--timeout-keep-alive控制的是一个 keep-alive 连接在完成一次请求后空闲多久会被 Uvicorn 关闭。默认值是 5 秒。这个值设太短客户端比如浏览器复用一个连接准备发第二个请求时发现连接已经关了就得重新建 TCP 连接握手成本变高吞吐受影响。设太长又会占着 socket 不释放在高并发慢客户端场景下容易把 worker 的连接数打满。我的实践建议是前端有 Nginx 时Uvicorn 只需对 Nginx 保持连接内网链路很快5 秒到 10 秒都合理如果服务直接面对公网没有 Nginx 挡在前面建议把 keep-alive 设得保守一些比如 2 到 5 秒同时务必设置--limit-concurrency。--limit-concurrency的作用是限制单 worker 同时建立的连接数防止恶意或意外的慢连接把进程内存和文件描述符耗尽。它配合--limit-max-requestsworker 处理 N 个请求后自动重启一起用能有效缓解长时间运行导致的内存增长问题。4.5 WebSocket 部署的额外功课如果你用 Uvicorn 跑 WebSocket 服务还有几个额外的点要注意。第一WebSocket 是长连接一个 worker 能同时承载的 WebSocket 连接数和它的文件描述符上限、内存直接相关worker 数也不是越多越好要根据业务并发评估。第二多 worker 部署后WebSocket 连接会被分发到不同 worker 进程进程之间内存不共享如果你在单个进程里维护了一份在线连接列表那广播消息时只有那一个进程里的连接能收到通知。正确做法是引入 Redis pub/sub 或者消息队列让所有 worker 订阅同一个频道收到消息后各自向自己进程内的连接推送。第三Nginx 反代 WebSocket 时需要显式配置 Upgrade 相关的头信息否则握手游不成功。这里给一个最基本的 Nginx 配置片段参考location /ws/ { proxy_pass http://uvicorn_backend; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection upgrade; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; }这些虽然不是 Uvicorn 本身的问题但只要是 Uvicorn 部署十有八九会和 Nginx 搭配提前把这些配好能少走很多弯路。5. 选型边界Uvicorn 与 Daphne、Hypercorn 的取舍5.1 三款主流 ASGI 服务器的定位差异Uvicorn 虽然是目前最主流的 ASGI 服务器但它并不是唯一的选择。圈子里还有 Daphne 和 Hypercorn各有各的擅长领域。我整理了一个简单的对比特性UvicornDaphneHypercorn维护方Tom Christie / EncodeDjango Channels 团队Quart 项目相关HTTP/1.1支持httptools 高性能支持支持HTTP/2不支持原生端到端支持支持HTTP/3QUIC不支持不支持支持WebSocket支持支持支持性能取向高性能优先稳定性优先协议全面优先推荐场景FastAPI/Starlette 生态Django Channels需要 HTTP/2/3 的场景Daphne 是 Django Channels 官方推荐的服务器和 Django 生态的集成最顺但更新节奏和性能表现相对中庸。Hypercorn 最大的卖点是协议支持非常全面HTTP/2、HTTP/3 都能端到端处理适合对现代协议有硬性需求的场景不过配置和使用门槛也更高一些。对于绝大多数 FastAPI/Starlette 用户Uvicorn 依然是最省心的默认选择因为这三个框架本来就是同一套技术栈。5.2 HTTP/2 的实际情况Uvicorn 不背这个锅很多人会问Uvicorn 支不支持 HTTP/2答案是目前并不原生支持。它默认的 httptools 解析器处理的是 HTTP/1.1h11 也是 HTTP/1.1 方向Uvicorn 目前没有端到端的 HTTP/2 协议栈。但在实际生产架构里这个能力缺口通常被上层代理填平了。标准做法是让 Nginx、Traefik 或者云上的负载均衡器终结客户端的 HTTP/2 连接然后后端用 HTTP/1.1 转发给 Uvicorn。客户端看到的是 HTTP/2性能和协议红利照样能吃到Uvicorn 只需要处理上游代理的 HTTP/1.1 连接就够了。只有在内部微服务之间也需要端到端 HTTP/2 的极端场景下才需要评估 Hypercorn。所以你在选型时先想清楚 HTTP/2 到底要在哪一段生效别为了一个前端已经解决的问题去换服务器。5.3 什么时候根本不该用 Uvicorn聊完优点也该说说哪些场景我不推荐用 Uvicorn。第一纯同步 Django 老项目。如果你没有 WebSocket、没有异步视图、没有 Streaming 响应需求那 Gunicorn 加普通同步 worker 反而更简单直接。强行引入 ASGI 服务器并不会让同步 ORM 查询变快反而多了一层复杂度。第二CPU 密集型的业务接口。Uvicorn 的协程模型在大量计算场景下帮不上忙因为 GIL 和单线程事件循环决定了它只能跑满一个核。CPU 密集型任务更合适的架构是开多进程、或者把重计算丢到独立的任务队列里去再异步回调结果。第三应用代码里大量使用同步阻塞调用的项目。如果一个项目里到处都是requests.get、time.sleep、同步数据库驱动的调用那么把服务器换成 Uvicorn 只会让你看到一个更明确的卡顿放大镜——因为所有阻塞都会堵住整个 worker。要么先把这些同步调用换成异步版本要么就不要硬上 Uvicorn先老老实实用同步服务器。5.4 版本管理别让 Uvicorn 的升级变成惊喜最后补一个经验Uvicorn 的版本更新速度并不慢升级时大版本之间的默认行为偶尔会有变化。我吃过一次亏某个项目从 0.20 升到 0.30 之后WebSocket 的实现由默认使用 wsproto 调整为自动优先选 websockets 库压测时握手行为有一点差异排查了两天才发现是版本切换带来的。所以线上环境我强烈建议把 Uvicorn 版本锁死写在requirements.txt或pyproject.toml的精确版本里比如uvicorn0.30.1不要用uvicorn0.30这种宽松约束。升级前先在测试环境专门跑一遍 WebSocket、长连接、优雅退出这三类场景的回归再决定要不要上生产。最后分享我自己生产环境常用的组合拳前端 Nginx中间 Gunicorn 管理进程worker 用uvicorn.workers.UvicornWorker框架是 FastAPI。如果是容器化部署则简化成裸跑 Uvicorn 单 worker把进程调度交给 K8s。压测时记得加--no-access-log或者把访问日志接到结构化日志采集里否则压测过程中的 access log 刷屏本身就能拖慢不少性能。再补一个小技巧启动命令里强烈建议加上--limit-concurrency按内存和业务情况设一个合理的并发上限宁可让多余的请求排队也不要让慢客户端把整个 worker 的内存拖爆。Uvicorn 是一台非常称职的异步发动机但驾驶它的还是你——把事件循环里那些同步阻塞的毛病改掉、把代理头和优雅退出的配置弄对它才能真正跑出你预期中的性能。