52、【Agent】【OpenCode】本地代理增强版分析(结束响应)

张开发
2026/4/17 3:53:29 15 分钟阅读

分享文章

52、【Agent】【OpenCode】本地代理增强版分析(结束响应)
【声明】本博客所有内容均为个人业余时间创作所述技术案例均来自公开开源项目如GithubApache基金会不涉及任何企业机密或未公开技术如有侵权请联系删除背景上篇 blog【Agent】【OpenCode】本地代理增强版分析Unix时间戳继续分析了本地代理中流式响应的处理就是直接存原始文本因为流式响应本质是多行文本SSE 格式无法直接转成单个 JSON 对象直接保存原始字符串就可以完整记录所有data事件然后是保存日志文件提到这里的文件名用时间戳来确保唯一性用的是 Unix 时间戳从 1970 年 1 月 1 日 00:00:00 UTC 开始到当前时刻所经过的毫秒数提到选择 1970 年 1 月 1 日是计算机领域的历史约定源于 Unix 操作系统的设计1970s 年初Unix 开发者需要一个统一的时间起点这是个全球统一时区无关的时间基准然后分析了其数据溢出的可能性分析了 JavaScript 的毫秒时间戳要到公元 28 万年才会超出安全整数范围当前是公元 2026 年不会发生整数溢出下面继续分析OpenCode上篇 blog 分析了日志文件是用 Unix 时间戳命名下面继续分析logEntry写入日志文件后其内容大致如下{timestamp:1712345678901,request:{/* 客户端原始请求 */},response:{/* 解析后的响应或原始字符串 */},error:null// 如果有错误会在这里记录}最后是结束客户端响应res.end();这里会通知 OpenCode 客户端DashScope 服务器的响应已全部转发完毕这是 HTTP 协议要求的最终结束信号这里面有个关键细节在proxyRes.on(data, ...)事件中数据已经通过res.write(chunk)逐块转发了响应内容这里res.end()只是关闭连接不传输任何新数据举个例子OpenCode 客户端通过非流式进行请求{model:qwen-plus,messages:[{role:user,content:你好}],stream:false}此时代理会记录下日志文件内容{timestamp:1712345678901,request:{model:qwen-plus,messages:[{role:user,content:你好}],stream:false},response:{id:chatcmpl-123,choices:[{message:{content:你好有什么问题吗}}]},error:null}另外可以看到本地代理并没有在data事件中直接结束响应而是等到了end事件有如下几个原因HTTP 要求必须等待所有数据接收完毕后才能结束响应流式场景需要持续转发 data 块直到结束中途不能直接结束响应错误处理如果中途出错比如网络中断得走 error 分支而非 end 分支OK最后再解释一个点无论是超时事件timeout监测还是error事件监测都对res.headerSent做了判断这里是防止本地代理重复发送 HTTP 响应头部信息不管是timeout事件还是error事件本地代理都可能已经向客户端发送了响应头信息比如典型的请求转发流程如下OpenCode 客户端 → 本地代理本地代理 → DashScope 服务器通过proxyReqDashScope 服务器返回响应头信息 → 本地代理立刻转发给 OpenCode 客户端通过res.writeHead(...)之后开始传输响应内容通过data事件此时关键点来了一旦执行了res.writeHead(...)在proxyRes的回调里res.headersSent就变成了true但如果在后续接收响应内容的过程中发生网络错误比如连接中断TLS 错误目标服务器一直没响应就会触发proxyReq.on(error)或者proxyReq.on(timeout)而此时客户端已经收到了响应头比如 200 OK那么此时不能再调用res.writeHead(502)或者是res.writeHead(504)否则会抛出异常Node.js 会 crash 崩溃也可能 warningOK本篇先到这里如有疑问欢迎评论区留言讨论祝各位功力大涨技术更上一层楼更多内容见下篇 blog【Agent】【OpenCode】代理日志解析整体结构

更多文章