Node.js TCP代理服务器处理大数据包崩溃:如何调整套接字缓冲区大小?
Node.js TCP代理服务器处理大数据包崩溃:如何调整套接字缓冲区大小?
我太懂你这种崩溃的糟心感了——用Node.js的net模块搭个TCP代理,本来好好的,结果碰到大数据包直接崩,试了一堆修改缓冲区的代码还没用,肯定头大。
先给你捋捋你之前尝试的问题在哪:
- 像
bufferSize、writableLength这些都是只读属性,你手动给它们赋值完全起不到作用,Node.js根本不会理这些修改 - 直接改
_writableState、_readableState这种带下划线的私有属性,属于硬改Node.js的内部实现,不仅跨Node.js版本可能失效,还容易搞出各种奇奇怪怪的未知问题,官方从来都不推荐这么干
接下来给你说正确的解决姿势,分几步来:
一、在创建阶段就配置缓冲区参数
别等套接字创建好再去改内部状态,要在创建服务器或者建立连接的时候,通过公开API配置缓冲区大小,这样才是合规且有效的:
比如创建代理服务器时,直接在net.createServer的选项里指定highWaterMark,所有新客户端连接都会继承这个配置;同时连接目标服务器的套接字也要同步配置:
const net = require('net'); // 创建代理服务器,直接配置缓冲区 const proxyServer = net.createServer({ highWaterMark: 256 * 1024, // 256KB,直接写数值更直观 // 要是你需要给可读、可写缓冲区分别设不同大小,也可以拆成下面这样: // readableHighWaterMark: 256 * 1024, // writableHighWaterMark: 256 * 1024 }, (clientSocket) => { // 连接目标服务器时,同样配置缓冲区 const targetSocket = net.connect({ host: '你的目标服务器IP', port: 目标服务端口, highWaterMark: 256 * 1024 }); // 用pipe双向转发数据,它会自动处理背压,比手动写data事件靠谱多了 clientSocket.pipe(targetSocket); targetSocket.pipe(clientSocket); // 重点!一定要加错误监听,不然出问题直接崩进程 clientSocket.on('error', (err) => { console.error('客户端连接出问题了:', err); targetSocket.destroy(); }); targetSocket.on('error', (err) => { console.error('连目标服务器出错了:', err); clientSocket.destroy(); }); }); proxyServer.listen(3000, () => { console.log('代理服务器跑在3000端口啦'); });
二、别忽略背压(Backpressure)的处理
很多时候大数据包崩溃,根本不是缓冲区太小,而是没处理好背压。简单说就是当接收数据的速度比发送数据快时,缓冲区会被填满,如果不暂停接收,就会溢出崩溃。
不过你要是用pipe方法的话,就不用手动处理了——Node.js已经帮你封装了背压的逻辑,会自动根据发送端的情况暂停/恢复接收数据。但如果你是手动监听data事件转发数据,一定要这么做:
clientSocket.on('data', (chunk) => { // 尝试写入数据,返回false表示缓冲区满了 const canContinueWrite = targetSocket.write(chunk); if (!canContinueWrite) { // 暂停读取客户端数据,避免缓冲区溢出 clientSocket.pause(); // 等发送端缓冲区空了(drain事件触发),再恢复读取 targetSocket.once('drain', () => { clientSocket.resume(); }); } });
还是那句话,能用pipe就用pipe,省得自己踩坑。
三、一定要捕获所有错误!
我见过太多人写TCP服务崩溃,就是因为没加error事件监听。Node.js里,如果一个EventEmitter的error事件没有被监听,进程会直接抛出异常崩溃。所以不管是客户端套接字还是目标服务器的套接字,都必须加上error事件的监听器,哪怕只是简单打个日志再销毁连接,也比直接崩进程强。
备注:内容来源于stack exchange,提问作者pro2stinger
相关产品推荐
相关产品推荐

