【声明】本博客所有内容均为个人业余时间创作,所述技术案例均来自公开开源项目(如Github,Apache基金会),不涉及任何企业机密或未公开技术,如有侵权请联系删除

背景

上篇 blog
【Agent】【OpenCode】本地代理分析(七)
提到了一个关键点,代理收到 Open构建了请求对象,然后发送请求内容后,代理就 return 退出了,解释了这里虽然 https.request 本身同步返回请求对象,但整个 IO 是异步的,涉及到 JavaScript 异步 + 闭包 + 流式处理中的核心机制:proxyReq.end 只是发出请求,并不代表请求完成,Node.js 的事件循环模块会持续监听网络事件,当目标服务器 DashScope 返回响应头时,Node.js 会自动调用回调函数 (proxyRes) => {...},即使外层函数已经 return,回调依然能安全访问 res 等变量,这就是闭包(Closure)在起作用,而 proxyRes 是由系统在回调触发时新创建的对象,下面继续分析

OpenCode

再解释下这里的接收回调函数

在这里插入图片描述

首先是入参 proxyRes这不是用户自定义的局部变量,而是 Node.js 在触发接收回调时,动态创建的参数,每次收到一个响应,系统都会新建一个 proxyRes 对象传进来,也就是说,这个对象在接收回调开始时才被定义生成

另外是内存安全的问题

  • 之前提到过,在运行时,JavaScript 的引擎会发现,内部接收回调函数使用到了外部的变量 res,于是引擎会保留对 res 变量的引用,即使外层函数(OpenCode 客户端对目标服务器的请求)返回结束,只要还有回调函数(指的是这里的接收回调函数)可能用到它,res 变量就不会被垃圾回收,这是 JavaScript 闭包的标准行为
  • 在这里,res(客户端响应对象)的生命周期由 Node.js 管理,一旦 proxyRes.pipe(res) 完成(之前说过,pipe 会自动 end)或出错,所有引用会自然解除,另外,当 HTTP 连接关闭时,res 也会被自动清理,所以也不会出现内存泄漏

最后总结下这里本地代理的关键点

  • 客户端 OpenCode 的请求函数 return 返回后,其定义的接收回调函数还能正常运行,Node.js 的事件循环模块会触发这个接收 callback 的调用
  • 客户端 OpenCode 的请求函数 return 返回后,其入参 res 变量还会继续存在,由闭包机制保持引用,以便接收回调函数实现透传
  • proxyRes 是接收 callback 被调用时新建的,不是用户自定义的变量
  • 没有内存泄漏问题,Node.js 会自动管理 HTTPS 连接的生命周期,这也是 Node.js 流式代理的标准做法

正是这种非阻塞式,事件驱动的模型,可以让 Node.s 能够高效处理成千上万的并发 AI 请求

再贴一次本地代理的实现

// dashscope-proxy.js

const http = require('http');
const https = require('https');

const server = http.createServer((req, res) => {
  console.log(`📥 Received ${req.method} ${req.url}`);

  if (req.method === 'POST' && req.url === '/v1/chat/completions') {
    let body = '';
    req.on('data', chunk => body += chunk);
    req.on('end', () => {
      // 从原始请求中获取 Authorization 头
      const authHeader = req.headers['authorization'] || '';

      const options = {
        hostname: 'dashscope.aliyuncs.com',
        port: 443,
        path: '/compatible-mode/v1/chat/completions',
        method: 'POST',
        headers: {
          'Authorization': authHeader, // 直接透传
          'Content-Type': 'application/json',
          'Content-Length': Buffer.byteLength(body)
        }
      };

      const proxyReq = https.request(options, (proxyRes) => {
        res.writeHead(proxyRes.statusCode, proxyRes.headers);
        proxyRes.pipe(res); // 自动处理流式/非流式 + 自动 end()
      });

      proxyReq.on('error', (e) => {
        console.error('Proxy error:', e.message);
        res.writeHead(502);
        res.end('Bad Gateway');
      });

      proxyReq.write(body);
      proxyReq.end();
    });
    return;
  }

  res.writeHead(404);
  res.end('Not Found');
});

server.listen(2048, '127.0.0.1', () => {
  console.log('✅ DashScope proxy with full OpenCode & Ollama compatibility running on http://127.0.0.1:2048');
});

OK,本地代理分析完后,把项目里 opencode.jsonapi 参数改一下,改成创建的本地代理 URL(这里设置的是 2048 端口号)

在这里插入图片描述

接着在终端输入

node dashscope-proxy.js

启动代理程序,可以看到端口已被代理程序占用
在这里插入图片描述
新建另一个终端,打开 OpenCode,输入

你是哪个模型?

可以看到 OpenCode 客户端的响应

在这里插入图片描述

同时本地代理进行了两次请求的转发

在这里插入图片描述

至于这里为什么会有两次请求,后面 blog 会进行分析


OK,本篇先到这里,如有疑问,欢迎评论区留言讨论,祝各位功力大涨,技术更上一层楼!!!更多内容见下篇 blog

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐