MongoCursorException 抓不到响应头?TaoToken 这样配进 Codex 的 config.toml 再查连接数
2026/9/18 23:05:21 网站建设 项目流程

MongoCursorException: couldn't get response header 刷满 PHP log。TaoToken 不做 MongoDB 的替代品:去 https://taotoken.net/?utm_source=taotoken_aicg_blog_end 创建一把 Key,把 Codex 的 config.toml 接上,拿它当排障搭子。真正要动手的地方还在本地——Mongo 侧的连接数为什么能冲到 20000、geo 搜索服务拿游标那段代码是不是漏了释放、官方那句「database 或 network 挂了时 driver 拿不到 response」到底覆盖了哪些情况,这些都得靠日志、代码和 serverStatus 的读数去对。Codex 在这里的角色是帮你把线索摆整齐,不是替你去连生产库。

这篇不写接入大全,只走一条线:先把 Codex 这条模型通道跑起来,再把 PHP 日志、mongo log 的连接数、取游标的代码一起丢给它做交叉对照,最后回到本地改连接池和超时,复现验证。

1. 从 PHP log 里那条 couldn't get response header 开始拆

线上最先看到的不是连接数,是Uncaught exception 'MongoCursorException' with message 'couldn't get response header'这条在 error log 里反复滚。业务侧表现是 geo 搜索接口变慢、部分请求 500,本地 mongo 已经连不上,重启之后能恢复一阵,过一段时间又复发。这类「重启就好」的现象特别容易被误判成单纯的网络问题,因为重启顺手清掉了连接。

1.1 mongo log 里 20000 的连接数说明了什么

mongo log 里最刺眼的是连接数冲到 20000 这个量级。正常业务如果连接池上限几十、PHP-FPM worker 数量有限,server 侧看到的current连接不该这么高。冲到两万通常意味着两件事之一:一是每个请求都新建连接、用完不归还;二是连接建了但卡在等响应上,socket 一直占着,driver 侧的超时又设得太长或者干脆没设。

老版 mongo 扩展抛的是MongoCursorException,新版 mongodb 扩展会换名字,抛MongoDB\Driver\Exception\ConnectionTimeoutExceptionExecutionTimeoutException一类。名字变了,根子常常还是同一个:driver 在等 socket 的响应头,等不到就抛。

1.2 官方那句「driver 拿不到连接响应」的边界

官方文档或 issue 里的说法大意是:当 database 或 network 出问题、driver 拿不到连接的响应时,就会抛出这个异常。这句话是对的,但太宽,宽到不能直接当结论用。它至少覆盖三种完全不同的情况——服务端进程被慢查询拖住、网络链路抖动丢包、以及客户端自己把连接池耗干导致后续请求拿不到可用连接。

三种情况的处理方式南辕北辙:慢查询要去看currentOp,网络抖动要看心跳和 RTT,连接池耗干要回去改maxPoolSize和超时。所以第一步不是改配置,而是分清楚眼前这次属于哪一种。分不清楚的时候,让 Codex 陪你对着材料过一遍,比在服务器上来回重启有效。

2. 给 Codex 接通道:config.toml 里改 model_provider 与 base_url

排障搭子得先能说话。Codex 默认走官方通道,额度、限流、切模型这几件事在排障期间最容易打断思路——你正对照日志,它告诉你请求超了。用 TaoToken 的兼容通道取代这一层,配置只落在 Codex 自己的配置文件里,和 MongoDB 那边完全不接触。

2.1 先建 Key,再决定模型 ID

打开 TaoToken 注册账号,进控制台创建一把 API Key。Key 只在创建时完整可见,复制下来先放到本地环境变量里,不要写进config.toml明文,更不要提交到代码仓库。

模型 ID 不要凭印象写。到 https://taotoken.net/?utm_source=taotoken_aicg_blog_end 的模型广场看当时列表里实际可用的 ID,把那一串原样复制。日志分析这种活儿不需要挑最贵的,上下文长度够放下几段日志和一段代码就行,具体以模型广场当时列表为准。

2.2 ~/.codex/config.toml 的可复制写法

Codex 的配置在用户目录下,文件名是config.toml。把下面的内容按你实际的 Key 和模型 ID 改好:

model = "YOUR_MODEL_ID" model_provider = "taotoken" [model_providers.taotoken] name = "TaoToken" base_url = "https://taotoken.net/api" env_key = "TAOTOKEN_API_KEY" wire_api = "chat"

这里的base_url必须是https://taotoken.net/api,末尾不要加/v1,也不要写成官网落地页地址。官网是给人点的,/api是给工具填的,这两个混了就会出现 404 或者路径拼错。Key 通过env_key指向的环境变量读取:

export TAOTOKEN_API_KEY="YOUR_API_KEY"

这行写进~/.zshrc~/.bashrc都行,重开一个终端确认echo $TAOTOKEN_API_KEY有值。如果你的 Codex 版本对wire_api取值更挑,先用chat试,报错再按版本说明调整。

2.3 保存后先跑一次通道自检

配置改完不要直接上日志。先在终端跑一句最简单的提问,看它能不能正常返回。返回了,说明通道通了;返回 401,多半是 Key 没读到或者复制时少了字符;返回 404,九成是把官网地址或者/v1写进了base_url

通道自检和 Mongo 排障是两件事,别混着调。通道不通就先修通道,通了再进下一节。

3. 把三份材料喂给 Codex:php log、连接数、取游标代码

材料给得糙,结论就会飘。日志、连接数、代码这三样各自说明一部分事实,单看任何一份都容易走偏。给 Codex 的时候尽量按下面的方式整理。

3.1 php log 与 mongo log 怎么截才有信息量

不要贴整个文件。从报错第一次出现的时间点往前推五分钟、往后取十分钟,这个窗口里通常能看到「先是变慢、然后开始抛异常、最后连接数往上爬」的顺序关系。把带时间戳的那几十行留下,尤其注意异常之间是否夹着别的警告。

mongo log 侧把连接数相关的行单独摘出来,标注是从什么时间点开始往上爬、涨到多少、有没有跟着出现连接被拒或者超时的记录。如果日志里有客户端 IP 或者端口,先做一次简单脱敏,把内网 IP 换成10.x.x.x这类占位。

把这两份贴给 Codex 时,明确告诉它这是 PHP 侧和 Mongo 侧的同一时间窗,让它先判断时间先后,再谈原因。顺序对了,结论的方向才不会反。

3.2 geo 搜索服务那段游标代码怎么脱敏再贴

geo 搜索这一段是重点。它大概是这样一个形状:按地理范围查一批点,拿到游标之后循环取数据,循环里可能还有别的查询或者外部调用。要贴的部分包括:创建连接或客户端的代码、查询语句本身、游标的遍历方式、以及异常处理和finally里的收尾逻辑。

脱敏只做三件事:连接串里的账号密码换成占位、内网地址换成占位、业务字段名如果需要保密就改成field_afield_b。不要为了省事把finally或者unset($cursor)之类的收尾代码删掉——连接泄漏的证据往往就藏在这些被删掉的行里。

贴的时候顺手说明一下调用频率:这个接口每秒多少次、每次查询大概返回多少文档。连接数冲到 20000,往往要有频率这个乘数才解释得通。

4. 逐项分辨:连接泄漏、慢查询堆住,还是网络抖动

三种可能性的排查动作不一样,但在日志层面会呈现出不同的形状。让 Codex 做的是帮你对照形状给出可能性排序和下一步该看什么,读数仍要你自己在本地或跳板机上跑。

4.1 连接泄漏在 serverStatus 里的形状

连接泄漏的典型表现是连接数单调上升、基本不回落,重启之后归零然后重新开始爬。判断的时候在本机或跳板机连上 Mongo,执行几条只读命令:

db.serverStatus().connections db.serverStatus().metrics.cursor db.adminCommand({ currentOp: 1, $all: true })

connections.currentavailable的比例,看metrics.cursor.open里的totalpinnedtimedOut是否长期偏高。如果打开的游标数一直不回落,多半是有代码拿了游标没读完也没关闭,或者在循环中途抛异常跳过了收尾逻辑。这类问题的修法在 PHP 代码里,不在 Mongo 参数上。

4.2 慢查询把连接池堆满

第二种是慢查询把连接占住。表现是连接数上去的同时,接口响应时间也跟着涨,而且涨得比较均匀,不是偶发尖刺。用下面这条找正在跑很久的操作:

db.currentOp({ "secs_running": { $gte: 3 }, "active": true })

重点看secs_runningplanSummaryns。如果同一类查询反复出现且扫描量很大,缺索引或者索引没被用上就是主要嫌疑;如果集中在 geo 查询上,看看是不是范围条件写得让索引失效了。这种情况改的是查询和索引,连接池上限往上调只会把问题推后。

4.3 网络抖动与「重启就好」的差别

第三种是网络层面的抖动。它的特征是间歇性、和业务量没有明显相关,日志里常常伴随连接被重置、等待响应超时这类记录。判断方法是看同一时间窗内其他服务有没有类似的连接异常,以及 driver 侧的心跳日志是否出现明显的间隔异常。

「重启就好」这四个字对网络抖动和连接泄漏都成立,所以它不能用来区分。区分靠的是重启之后的曲线:泄漏是从零开始稳步爬升,抖动是重启后曲线正常,过一段时间因为同一条链路的问题再次出现。

5. 结论回到本地:连接池上限和超时怎么改

Codex 给出的是一份可能性排序和下一步的观察点,真正动手改配置、重启服务、跑复现脚本,都在你本地的 PHP 和 Mongo 环境里。改之前先把当前参数记下来,改完至少观察一个完整业务高峰。

5.1 PHP 侧连接参数

新版 mongodb 扩展的写法大致是这样,参数含义按你实际版本对照官方说明调整:

<?php $client = new MongoDB\Client( 'mongodb://10.x.x.x:27017', [ 'connectTimeoutMS' => 2000, 'socketTimeoutMS' => 15000, 'serverSelectionTimeoutMS' => 3000, 'maxPoolSize' => 50, ], [ 'typeMap' => ['root' => 'array', 'document' => 'array', 'array' => 'array'], ] );

老版扩展用的是MongoClient,参数名不完全一样,pool_sizeconnectTimeoutMSsocketTimeoutMS这几个含义相近但行为有差别,别直接套。核心思路是给超时设一个明确的、比上游接口超时更短的值,让卡住的连接能尽早被回收,而不是一直挂着等。

5.2 本地复现与验证节奏

复现方式建议用脚本压测那个 geo 接口,同时开着db.serverStatus().connections的轮询,看连接数在多大并发下开始明显偏离基线。改完参数后跑同样的脚本,对比曲线,而不是只看「这次没报错」。

验证要分两步:先确认异常不再出现,再确认连接数回到了合理区间。只满足第一条不算修好,可能只是超时把它盖住了。

6. 通道跑通之后,去控制台对一下这次调用

配置保存之后,先在 TaoToken 模型对话 里用同一把 Key 发一条测试消息,确认模型 ID 和 Base URL 没填错——排障中途最怕的是通道本身在悄悄报错,你却以为是模型答得不对。如果日志分析要连着跑几天,可以打开 Coding Plan 看套餐够不够用,Key 在 控制台 API Keys 里创建和轮换。

顺手打开 https://taotoken.net/?utm_source=taotoken_aicg_blog_end 的用量页面,对一下刚才那几次提问有没有记上账。Mongo 那边的连接池调完,通常还要观察一两个高峰才敢说稳;这段时间里 Codex 的通道保持可用,比临时去开一个额度要省事得多。更细的接入参数对照可以看 Claude Code 接入文档,环境变量和配置文件两种方式都能对着抄。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询