PHP事务内禁止调用外部API:长事务、锁与最终一致性重构指南
PHP事务内禁止调用外部API这句话我在代码评审里强调过很多次自己早年间也因为没守住这条底线吃过亏。当时做订单系统为了省事把支付下单、库存扣减、短信通知全塞进同一个数据库事务里上线后第一次大促就对账对得头皮发麻——用户那边钱扣了本地订单表里却查不到记录。今天就用庖丁解牛的方式把这个原则从原理到实践完整拆一遍为什么事务里不能调外部接口外部副作用会在哪个环节咬人已经踩进坑里的代码又该怎么一步步重构出来。这篇东西适合写PHP事务的开发者读也适合那些review别人代码时总觉得“有问题但一时说不清哪里有问题”的朋友。1. 先想清楚事务的本质它到底能保护什么1.1 一条数据库事务的完整生命周期一条事务从begin开始到commit或rollback结束。在这期间数据库为它分配事务ID记录undo日志锁住它操作过的行在特定隔离级别下还要维护一个一致性快照。事务的初衷很纯粹保证同一数据库连接内的一组本地写操作要么全部生效要么全部不生效。注意我这里特别强调了“本地数据库操作”。很多同事写代码时会把事务当成业务逻辑的“保险箱”觉得把支付、库存、通知全部包进去它们就能和订单数据一起成功、一起失败。这是个非常普遍的误解。事务不是分布式协调器它做不了跨系统的原子性。数据库事务能控制的只有当前连接、当前实例里那个数据库文件的数据变化它控制不了网络另一端任何一个服务的状态。把外部API调用塞进事务相当于厨师炒菜炒到一半关火跑出去买菜锅里的菜确实是你的但外面那家菜市场可不听你的指挥你去了不一定买得着买回来你的菜也可能已经糊了。1.2 外部API调用的本质不可控的等待那么什么是“外部API”这里的“外部”不单单指第三方更准确地说是指任何经过网络传输、不由当前数据库事务掌控的调用第三方支付平台的下单接口、库存服务的扣减接口、用户中心的RPC、短信邮件推送网关都算。它们的共同特点是延迟不可控、结果不可预知。一次普通的HTTP请求从DNS解析开始TCP握手、TLS协商、服务端业务处理、响应返回每一个环节都可能消耗几十毫秒甚至几秒。更麻烦的是超时的时候你根本不知道对方到底执行了没有——有时候是服务端已经扣款成功只是响应报文在网络上丢了。这种不确定性一旦放进数据库事务里事务原本“短小快”的特点就被彻底破坏数据库连接被长时间占用行锁被长时间把持整个系统的吞吐量会被一根外部请求拖垮。2. 三宗罪长锁、连接耗尽和无法回滚的副作用2.1 行锁和表锁被无限期持有先看最常见的场景。事务A更新了订单表里某一行的状态紧接着调用外部支付API支付网关平均耗时1.5秒。就在这1.5秒里事务B想给同一行订单更新一个取消标记它就只能阻塞在锁等待状态。如果并发量上来排队等锁的会话越来越多InnoDB的锁等待超时参数一旦到了临界点业务就会报出典型的Lock wait timeout exceeded; try restarting transaction。最迷惑的地方在于你去查那条update语句它本身执行只要1毫秒但整个事务从开启到提交横跨了几秒钟这1毫秒的活被无限拉长数据库表面负载不高业务却大量报错。我帮人排查过很多线上的长事务问题十有八九都能在事务范围内找到HTTPClient的影子。长事务最阴险的一点是它不一定会立刻报错而是让锁和连接像慢性病一样越积越多直到某个时间点集中爆发。2.2 连接池被“慢事务”占满PHP-FPM模式下每个PHP进程通常持有自己的数据库连接在连接池模式里连接数量更是有限资源。一个请求在事务里调外部API阻塞3秒这条数据库连接就被占用3秒期间它做不了任何别的事情。做个粗算假设连接池有20个连接20个请求同时命中这种事务内调API的代码连接池瞬间打满后续所有请求排队等待拿连接。表现就是接口RT从几十毫秒暴涨到几十秒数据库服务器的CPU可能还很闲但客户端就是等不到响应。这是非常典型的应用层错误最后往往被别人误判成数据库性能问题浪费一堆时间去看慢查询、加索引其实换个角度看代码两分钟就能定位到根因。用生活化的比喻就是修车铺工位全占着每台车都要等师傅外出采购零件出去的师傅没回来后面的车全部堵在门口铺子里却没有一个工位在真正干活。2.3 回滚困境数据库能回滚外部副作用不会这是事务内调用外部API最致命的问题。看一段典型伪代码$pdo-beginTransaction(); try { insertOrder($order); $payResult callPayApi($order); // 外部API调用 decreaseStock($order-sku); $pdo-commit(); } catch (\Throwable $e) { $pdo-rollBack(); throw $e; }问题在于如果decreaseStock失败触发了rollBack数据库里刚插入的订单和库存会一起回滚看起来一切干净了。但支付平台那边的钱已经真实扣掉了。数据库的rollback能撤销本地行却没有能力撤销外部已经扣款的动作、已经发出的短信、已经扣减的积分。这就是分布式系统里典型的“部分成功”状态外部成功、本地失败。更隐蔽的是哪怕把支付调用挪到事务的最后一条commit之前数据库任何一个环节报错依然存在这个窗口。所以结论只有一个只要外部调用发生在事务提交之前不一致就不可避免。3. 高频反模式现场订单、积分、短信这三个坑最典型3.1 下单、支付、库存一锅端上面那段伪代码就是下单场景最常见的反模式。它一次踩了前面说的三个坑支付API耗时长事务把订单行锁得死死的支付一旦成功但本地后续SQL失败用户付了钱订单却没生成库存扣减放在支付之后高并发场景下还容易超卖。这个场景里多数产品经理要求的是“用户下单必须保证库存、支付、订单三件事一致”。但正确做法不是把它们放进同一个数据库事务而是先落本地订单订单状态标记为“待支付”立刻提交事务释放锁事务提交成功后再去调支付API最终以支付回调的结果为准更新订单状态为“已支付”或者“支付失败”并配合库存预留和超时释放策略来做。3.2 积分和余额的跨系统同步积分系统的账户往往在另一个数据库甚至完全另一个服务。但很多PHP项目为了省事会在用户充值、下单扣积分的过程中于同一个本地事务里同步调用积分服务。这样的代码积分服务一次抖动主链路整体失败积分服务超时主库事务被拖成慢事务。我见过一个案例某个会员日活动中积分服务因为缓存穿透变慢结果主订单服务的数据库连接池被全部占满整个下单入口瘫痪了将近半小时。后期拆成事件异步同步之后积分服务再怎么抖动主流程都纹丝不动。正确的姿势是本地事务只写用户在本库的流水和余额变化同时在同一事务里插入一条待发送的事件记录提交之后再通过队列或事件分发器把“用户积分解冻/扣减”的消息发出去由积分服务自行消费并通过幂等保证只处理一次。3.3 短信和推送“顺手”放进事务里短信、邮件、App推送这类通知是事务内调用外部API里最没有必要、危害却很大的一个。短信网关可能因为手机号格式错误、通道限流、供应商故障等各种原因报错而且第三方通道超时情况非常常见。如果你在事务里调用短信服务一条短信超时整个订单回滚。我做过的项目中真实发生过后台管理员批量操作时事务里带了短信群发短信通道抖动导致事务一直不提交结果旁边一张高频访问的数据表被锁影响前台用户看到的是整个服务不可用而不是“短信没发出去”。这种场景几乎没有任何业务需要把通知和核心数据放进同一个原子操作里。通知本来就应该走异步丢失最多补发一条完全不值得为它牺牲系统稳定性。4. 重构落地方案把事务边界画在副作用之外4.1 核心原则本地事务只做本地的事重构后最核心的一条原则是“凡是会经过网络的调用一律放到commit之后”。把上面的下单例子改成这样$pdo-beginTransaction(); try { insertOrder($order); $pdo-commit(); } catch (\Throwable $e) { $pdo-rollBack(); throw $e; } try { callPayApi($order); } catch (\Throwable $e) { // 记录日志发起补偿或进入重试队列 markOrderPayFailed($order-id, $e-getMessage()); }事务提交完之后锁释放了连接归还了这时候再做外部调用外部服务慢一点、挂掉一点影响范围都被限制在一个请求内不会传导到数据库层。但代价是你失去了“数据库回滚兜底”的幻觉必须主动去处理“订单已经落库、支付调用失败”的后续逻辑。4.2 本地消息表加异步Worker最稳妥的落地姿势如果希望更鲁棒可以引入本地消息表也有人管它叫 outbox 模式。思路很简单业务数据和待发送消息在同一个库、同一个事务里写入提交后由独立的Worker扫表把消息逐个投递给外部API成功后更新消息状态为已发送。$pdo-beginTransaction(); try { insertOrder($order); insertOutboxMessage([ topic order.paid, payload json_encode($order), status pending, ]); $pdo-commit(); } catch (\Throwable $e) { $pdo-rollBack(); throw $e; }这里有个很多人没注意到的关键点业务表和消息表必须在同一个库、同一个连接、同一个事务里写。如果把消息插入放到事务外那消息表本身就面临“订单写成功了消息没写进去”的问题跟直接调API又有什么区别呢出队Worker消费时需要做幂等防止重复投递。消息表至少要有这些字段id、topic、payload、status、retry_count、next_retry_time、create_time。Worker扫描statuspending且next_retry_time now()的记录调用外部API成功置sent失败则累加重试次数并设置指数退避的next_retry_time。这套方案同样不完美它解决的是“外部调用在事务外可靠执行”的问题但引入了一个新的分布式场景外部API成功了可Worker更新status之前crash了消息会被重投。所以幂等是最后一道防线外部API必须接收一个唯一的request_id或业务单号来做去重。4.3 最终一致性与补偿状态机、对账、幂等本地消息表是手段而不是目的业务上真正需要的是最终一致性。所以我习惯在重构时把业务状态设计成一个小型状态机。以支付为例订单初始状态为“待支付”事务里只写这个状态。支付回调到达后事务里把状态改成“已支付”同时扣减库存如果外部调用失败达到上限把状态改成“支付失败”或者保持“待支付”并进入定时对账任务。对账任务的作用是每天定时拉取外部服务账单和本地订单状态比对凡是外部已扣款但本地还是“待支付”的进行补偿处理要么补全本地流程要么调用退款接口。幂等设计上我建议对每一个外部动作生成一个biz_id规则可以是业务表主键加动作类型例如order:12345:pay。外部服务拿这个ID去重重试多少次都不会产生重复扣款。4.4 重构前后对比用数据感受一下差异拿我经手过的一个订单模块来举例。重构前用户点“提交订单”代码依次执行插入订单、调支付API、扣库存、发短信全部包在一个事务里。线上真实数据支付API平均耗时800ms短信接口时不时飘到2秒整个事务最慢能拖到3秒多。重构后事务里只有一次insert提交耗时稳定在30ms左右事务提交后异步投递“已下单”消息由Worker负责调用支付API和短信网关。两者的对比可以看下面这张表维度事务内同步调API事务外异步投递事务持锁时间支付网关耗时短信耗时秒级本地insert耗时毫秒级连接占用长事务长期占用连接事务提交即释放连接外部服务抖动直接拖垮主流程仅消息积压可重试一致性保证看似一致实际回滚不了外部副作用状态机补偿最终一致系统复杂度低增加消息表和Worker但可控大多数业务系统并不需要跨服务强一致能真正做到强一致的手段也就是XA协议而且XA在微服务和跨网络环境下成本极高。对API调用这种天然不可控的操作接受最终一致性反而更容易保证整体可用性。5. 绕不开时的过渡方案超时、熔断与妥协姿势5.1 如果只能同步调用至少要做超时和熔断总有些老系统一时半会重构不了外部调用暂时必须同步出现在业务链路里。这种情况下至少两条底线必须守住超时和熔断。PHP这边无论是用原生curl还是Guzzle都必须显式设置连接超时和总超时。比如curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 1); curl_setopt($ch, CURLOPT_TIMEOUT, 3);。没有超时的HTTP调用一旦对方网络异常请求可能几分钟不返回一个事务就卡上几分钟。同时调用方需要做熔断降级。连续失败阈值到了就快速失败不再把请求打向外部的脆弱服务给数据库连接一条生路。这是短期止血手段不代表你可以一直这样写后续该重构还是要重构。5.2 重试绝不能塞进事务里有些人为了增强“可靠性”会在事务内写循环重试这等于给事故按了加速键。一次失败重试3次每次超时5秒事务持有时间直接变成15秒锁和连接都炸了。即便要同步重试也应该把重试逻辑放到事务提交之后通过标记来记录处理进度。有一个过渡阶段的妥协做法事务内只把订单标记成“待发送通知”commit完成后再在代码层面读取这个标记执行外部调用。这样事务不受外部延迟影响只是代码整体上还是同步链路。它能保证数据库连接不被外部调用占用但依然无法处理“通知服务失败”的情况所以必须配套重试队列和日志记录。5.3 别把内部RPC当成“自己人”就掉以轻心很多团队觉得内部服务之间的RPC不算外部API延迟低、可控性高放事务里问题不大。这是错觉。判断一个调用该不该放进事务标准不是“是不是我的服务”而是“是否经过网络传输、是否可能长时间等待”。内部RPC同样有网络开销和超时同样会让当前事务变成慢事务。更麻烦的是内部服务之间容易出现交叉调用A事务调B服务B服务又回查A服务的数据库一旦双方都在事务里等待对方就可能形成跨服务死锁排查难度比单库死锁高不少。所以我的建议是即使是内部RPC也尽量放在事务提交之后除非你确认它对延迟极度敏感且经过严格压测。6. 排查实录怎么从代码和数据库里揪出事务内外部调用6.1 用慢查询和processlist定位长事务代码里藏着外部API调用的事务最典型的表现就是“SQL执行很快事务整体很慢”。开启慢查询日志long_query_time设成1秒这时候你看到的可能不是某条SQL特别慢而是请求日志里一条接口从进入到响应隔了好几秒。我常用的定位手段是开另一个终端执行SHOW FULL PROCESSLIST;看连接的状态。如果一个连接显示Sleep但通过information_schema.innodb_trx查到它有一条未结束的事务且trx_started已经是好几秒甚至几十秒之前那基本可以断定事务在等某个事务外的东西。配合代码搜索在这个时间窗口里有没有HTTP调用、RPC调用几乎一抓一个准。6.2 死锁和锁等待的具体排查命令当业务已经报出锁等待超时或者死锁错误可以先看SHOW ENGINE INNODB STATUS\G输出里的LATEST DETECTED DEADLOCK段落它会告诉我们最近一次死锁涉及哪两张表、哪两条语句、哪个事务最后持有的锁。更系统的做法是查这三张系统表information_schema.innodb_trx查看所有正在运行的事务innodb_lock_waits查看谁在等谁的锁innodb_locks查看锁明细。我一般三步走先把trx_time超过10秒的事务挑出来再拿事务对应的SQL去代码里搜最后看事务代码段里有没有外部调用。三步走完问题基本水落石出。6.3 常见问题与排查思路速查现象排查思路常见根因接口偶发超时数据库CPU不高看processlist慢连接和innodb_trx长事务内调用外部API锁被长时间持有数据库连接池被打满监控连接占用曲线对应时间点的请求日志慢事务持有连接时间过长用户被扣款但订单未生成本地事务日志和外部API日志比对外部副作用不受数据库事务回滚控制定时任务重复发放积分检查外部API幂等键是否一致重试机制没有幂等保障一个短信网关故障拖垮整站追调用链看事务范围事务内包含通知类外部调用排查这些问题的核心思路是不要只看SQL本身。数据库往往只是受害者根因在应用层的代码设计里。下次再看到接口监控图上那些毛刺先别急着让DBA加索引去事务边界里看看有没有网络调用经常能省下一整晚的排查时间。最后分享一点我自己的体会把外部调用移出事务技术上其实不难难的是产品经理和业务方总想让你“一步到位”恨不得在一次请求里把支付、库存、积分、短信全同步搞定。我现在的习惯是每次需求评审先把事务边界画出来凡是会经过网络的调用一律放到事务外用状态机、重试队列和定时对账来兜底。刚开始大家都嫌麻烦直到某天外部通道一次抖动别人家的服务挂半天我们的系统稳如磐石团队里的质疑声立刻就消失了。另外还有一个小技巧事务commit之后再发通知catch里记录日志并且把一条失败消息写到本地队列表配合定时对账任务能解决90%因外部调用导致的不一致问题。希望这篇拆解能帮到正在被长事务和分布式一致性折磨的PHP开发者。

相关新闻

UE4游戏Debug框架:构建状态锚点与数据流图的立体诊断体系

UE4游戏Debug框架:构建状态锚点与数据流图的立体诊断体系

1. 项目概述:为什么“Debug 游戏”不是修bug,而是重建信任链在UE4项目推进到Alpha后期或Beta验证阶段,你大概率会遇到这样一种沉默的崩溃:玩家反馈“进地图就卡住”,QA提交的截图里只有半截UI和一个静止的旋转加载图标…

2026/10/15 21:50:01 阅读更多 →
YOLOv1目标检测算法解析:从原理到PyTorch实现

YOLOv1目标检测算法解析:从原理到PyTorch实现

做目标检测的都知道,在2015年之前,想用深度学习做检测基本绕不开两条路:要么用滑动窗口遍历整张图,要么走R-CNN那套区域提议加分类的路子。滑动窗口慢到让人怀疑人生,R-CNN虽然精度不错,但训练流程拆成候选…

2026/10/15 21:50:01 阅读更多 →
共享汽车管理系统实战:SpringBoot2+Vue3+MyBatis-Plus+MySQL8.0开发解析

共享汽车管理系统实战:SpringBoot2+Vue3+MyBatis-Plus+MySQL8.0开发解析

做共享汽车运营系统,最头疼的往往不是算法有多复杂,而是怎么把一堆散落的业务环节串成一条顺畅的链路。车辆要能被检索、下单、解锁、计费、归还、结算,每一步都牵扯状态变更和订单流转,稍不留神就会在并发场景里出现数据打架。我…

2026/10/15 21:49:00 阅读更多 →

最新新闻

GEE交互式碳固存潜力地图:土壤属性评估与优先补碳地块筛查

GEE交互式碳固存潜力地图:土壤属性评估与优先补碳地块筛查

做区域碳汇潜力摸底的时候,我最头疼的往往不是报告怎么写,而是怎么让决策者一眼看懂“哪些地块值得优先补碳”。单看一张土壤有机碳含量图不够——含量低的地方可能受限于砂质、干旱,实际提升潜力很小;含量高的湿地和林地又可能已…

2026/10/15 22:43:10 阅读更多 →
GESP八级真题复盘:位运算、线段树、分层图与树形DP全解析

GESP八级真题复盘:位运算、线段树、分层图与树形DP全解析

这几天正好在帮几个学生复盘2025年3月这次GESP八级认证的真题,自己也把卷子从头到尾重新过了一遍。说句实话,这一批次的八级题目整体难度比前两次要收敛一些,但陷阱埋得挺深,尤其是第二题和第四题,表面上看着是常规考法…

2026/10/15 22:43:10 阅读更多 →
AI时代的信贷业态:从评分卡到大模型的智能风控全解析

AI时代的信贷业态:从评分卡到大模型的智能风控全解析

我最早接触信贷业务时,审批靠的是一张纸质申请表、一沓流水复印件,以及审批人凭经验给出的判断。那时候谁也不会想到,十几年后“AI 时代的信贷业态”会成为信贷业务概述里最绕不开的一节。AI能做的事早已不止是给客户打个分,它正在…

2026/10/15 22:43:10 阅读更多 →
用Python验算《量子计算和量子信息》课后习题:框架与避坑指南

用Python验算《量子计算和量子信息》课后习题:框架与避坑指南

简介:《量子计算和量子信息》(尼尔森和丘昂著,赵千川译)是一本量子计算领域的经典教材,这份课后答案由 GitHub 用户 goropikari 整理发布,属于非官方解题手册,适合修读相关课程、自学量子计算或…

2026/10/15 22:43:10 阅读更多 →
终端实战:本地大模型多版本部署与量化选型指南

终端实战:本地大模型多版本部署与量化选型指南

1. 先想清楚:为什么要在终端里管多版本大模型 1.1 我建议把方案定在终端而不是图形界面 以前我在本机折腾大模型的时候,最头疼的往往不是模型效果,而是版本管理。同一个开源模型,今天出一个4bit量化版,明天社区又有人…

2026/10/15 22:43:10 阅读更多 →
Shell脚本进阶实战:参数处理、错误捕获与幂等性设计

Shell脚本进阶实战:参数处理、错误捕获与幂等性设计

1. 从"能跑就行"到"跑得漂亮":Shell 脚本的进阶分水岭很多人学 Shell 脚本都经历过这样一个阶段:能写个for循环批量改文件名,能用if判断文件是否存在,就觉得自己"会 Shell"了。但真到了生产环境里&…

2026/10/15 22:42:09 阅读更多 →

日新闻

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

1. 为什么选择容器化方式部署 LoRa 网络服务LoRa 物联网项目落地时,网络服务器是整个链路的核心枢纽。它负责接收网关上报的数据包、完成去重与解密、再把有效载荷分发给上层应用。早期大家习惯把网络服务器直接装在物理机上,依赖一堆系统库和特定版本的…

2026/10/15 0:00:46 阅读更多 →
共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

1. 共享推理服务里,扩缩容到底难在哪做过大模型推理服务部署的人,大概都经历过这样的场景:一个团队共用一套推理集群,A 业务跑的是 7B 的小模型,B 业务跑的是 72B 的大模型,C 业务白天流量高、晚上几乎归零…

2026/10/15 0:00:46 阅读更多 →
Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

1. 引子:一次内核恐慌引发的追查先从一个真实场景说起。某次我给一台老旧的嵌入式设备折腾系统裁减,为了把启动时间压进两秒以内,我手贱地把一串自认为“没用”的 systemd 服务给 disable 了,其中就包括systemd-remount-fs.servic…

2026/10/15 0:00:46 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/15 16:18:54 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/15 19:41:51 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/15 13:48:15 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 0:14:36 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 22:45:47 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 9:49:14 阅读更多 →