【声明】本博客所有内容均为个人业余时间创作,所述技术案例均来自公开开源项目(如Github,Apache基金会),不涉及任何企业机密或未公开技术,如有侵权请联系删除
背景
上篇 blog
【Agent】【OpenCode】本地代理分析(效果呈现)
分析了其中的接收回调函数,提到了入参 proxyRes 不是用户自定义的局部变量,而是 Node.js 在触发接收回调时,动态创建的参数,每次收到一个响应,系统都会新建一个 proxyRes 对象传进来,另外分析了内存安全的问题,提到客户端响应对象 res 的生命周期由 Node.js 管理,一旦 proxyRes.pipe(res) 完成,或者当 HTTP 连接关闭时,res 会被自动清理,不会出现内存泄漏,最后总结了下本地代理的关键点,正是这种非阻塞式,事件驱动的模型,可以让 Node.s 能够高效处理成千上万的并发 AI 请求,然后改掉 opencode.json 中的 api 为本地代理的 URL,启用本地代理,再打开 OpenCode 客户端,发送请求,可以看到代理的转发过程,下面继续分析
OpenCode
上篇 blog 已经演示了本地代理的启动和运行,下面最后再解释下 http.createServer 的工作机制,以及它和 https.request 接收回调之间的关系


首先,http.createServer 创建的是一个请求处理函数
javascript
const server = http.createServer((req, res) => {
// 请求处理函数(Request Handler)
});
其中
- 返回值 :一个
http.Server实例(server.listen可监听端口) - 参数
(req, res) => {...}:本质也是个回调函数(就像https.request创建的接收回调一样),Node.js 称之为请求监听器(request listener)
每当有客户端(OpenCode)发起 HTTP 请求到本地代理时,Node.js 就会自动调用这个函数,简单来说,createServer 创建的是当有用户访问时,要做什么的逻辑
然后是触发条件,只有当外部客户端,比如 OpenCode,浏览器,curl 命令向本地代理的 Node.js 服务器发起 HTTP 请求时,才会触发 ,举个例子
- 运行服务器:
server.listen(2048) - OpenCode 客户端发送请求:POST
http://localhost:2048/v1/chat/completions - Node.js 收到 TCP 连接,解析 HTTP 请求
- 自动调用
createServer定义的请求处理函数(req, res) => {...},其中req为 OpenCode 发来的请求对象(包含 method,url,headers,body 等),res为要写回给 OpenCode 的响应对象
关键点:(req, res) => {...} 只被入站请求触发,链路如下

然后是目标服务器 DashScope 的响应,接收 DashScope 的响应和 createServer 就没关系了,也不会触发 (req, res) => {...},而是触发 https.request 定义的接收回调函数 (proxyRes) => {...},所以总结下这两个事件如下
- OpenCode 客户端:负责发送请求,处理函数为
http.createServer创建的(req, res) => {...},由 Node.js 调用,触发时机为收到外部 HTTP 请求 - DashScope 目标服务器:负责返回响应,处理函数为
https.request创建的(proxyRes) => {...},同样由 Node.js 调用,触发时机为收到上游服务器的 HTTPS 响应
其数据流向如下

可以看到,这两个回调是独立的,一个是主动请求后结果处理,本地代理去找目标服务器,另一个则是被动接收,目标服务器来找本地代理
另外,每个请求都有独立的作用域和生命周期,即使同时有 100 个 OpenCode 请求,每个请求都会有自己的 (req, res) 和自己的 https.request,Node.js 通过闭包和事件循环确保数据彼此之间互不串扰
javascript
http.createServer((req_from_opencode, res_to_opencode) => {
// 这个作用域只属于 OpenCode 的这一次请求
const proxyReq = https.request(dashscope_options, (res_from_dashscope) => {
// 这个回调属于此次向 DashScope 发起的请求后的响应
// 但它能访问外层的 res_to_opencode(闭包!)
res_to_opencode.writeHead(res_from_dashscope.statusCode, ...);
res_from_dashscope.pipe(res_to_opencode);
});
proxyReq.end(body);
});
OK,本篇先到这里,如有疑问,欢迎评论区留言讨论,祝各位功力大涨,技术更上一层楼!!!更多内容见下篇 blog
【Agent】【OpenCode】本地代理增强(日志记录)