用Nodejs做一个http代理服务器的问题
用Nodejs做一个http代理服务器的问题
我做的这台代理服务器作为网关,把来自公网客户端的请求通过http转发到内部的连天服务器,但在开发阶段由于客户端发送带未转义的双引号,换行符等字符,导致聊天服务器解析json数据包出错,从而未响应我的网关服务器。
我于是设置了请求超时,和错误事件处理,以结束客户端的请求。但即使这样,node写的网关服务器还是经常被挂起,即进程和端口一直在,但就是不能再响应来自客户端的请求了,最后返回server no reult。初次使用nodejs写,没有系统学习过,请大神帮忙看看我这代码有啥问题。
<pre class=“prettyprint language-javascript”> <code> process.on(‘uncaughtException’, function (err) { console.log('Caught exception: ’ + err); });
var config=require(’./config.json’); if(config){ for(var k in config){ console.log(k+’:’+config[k]); } } var LOCAL_PORT = config.localPort || 1337; var REMOTE_PORT = config.mailchatServerPort || 7777; var REMOTE_ADDR = config.mailchatServer || ‘10.25.2.17’;
var log4js = require(‘log4js’); log4js.configure({ appenders: [ { type: ‘console’ }, //{ type: ‘file’, filename: ‘logs/cheese.log’, category: ‘cheese’ }, { type: ‘dateFile’, filename: ‘info.log’, pattern: ‘-yyyy-MM-dd’, alwaysIncludePattern: false, category: ‘dateFileLog’ } ], replaceConsole:true, levels:{ dateFileLog: ‘INFO’ } });
var log = log4js.getLogger(‘dateFileLog’);
http = require(‘http’); http.createServer(onRequest).listen(LOCAL_PORT); log.info('Gateway Server listen at ’ + LOCAL_PORT + ‘.’);
function onRequest(client_req, client_res) { if(client_req.url.indexOf(’/favicon.ico’)==0) return; log.info(‘Request URL: ’ + client_req.url); log.info(‘Request Header: ’ + JSON.stringify(client_req.headers)); var options = { hostname: REMOTE_ADDR, port: REMOTE_PORT, path: client_req.url, method: client_req.method, headers: client_req.headers }; client_res.setHeader(‘Content-type’,‘application/json’); client_res.setHeader(‘Access-Control-Allow-Origin’,’*’); var proxy = http.request(options, function (res) { log.info('From ChatServer STATUS: ’ + res.statusCode); log.info('From ChatServer HEADERS: ’ + JSON.stringify(res.headers)); res.setEncoding(‘UTF8’); res.pipe(client_res, { end: true }); });
proxy.on(‘error’, function(e) { log.info('Problem with request from ChatServer: ’ + e.message); client_res.statusCode=500; client_res.end(); //client_res.end('cache server error: '+e.message); });
proxy.setTimeout(10*1000, function(){ log.info(‘Request Timeout From ChatServer in 10 seconds, then response code 500’); client_res.statusCode=500; client_res.end(); //client_res.end(‘Reuqest Timeout’); });
client_req.setTimeout(10*1000, function(){ log.info(‘Timeout’); client_res.statusCode=500; client_res.end(); });
client_req.pipe(proxy, { end: true });
} </code> </pre>
针对您提到的问题,我们可以从几个方面来优化您的代码,并解决一些潜在的问题。主要关注点包括处理特殊字符、设置超时机制以及错误处理。
问题分析
- 特殊字符处理:客户端发送的JSON数据包含未转义的双引号和换行符,导致聊天服务器解析失败。
- 超时机制:您已经为请求设置了超时,但是仍然遇到服务器挂起的问题。
- 错误处理:虽然您已经添加了错误处理,但是可能还需要更细致的处理逻辑。
示例代码及说明
const http = require('http');
const log4js = require('log4js');
// 配置日志
log4js.configure({
appenders: [{ type: 'console' }],
levels: { default: 'INFO' }
});
const log = log4js.getLogger();
// 加载配置文件
try {
const config = require('./config.json');
for (let key in config) {
log.info(`${key}: ${config[key]}`);
}
} catch (e) {
log.error('Failed to load config file', e);
process.exit(1);
}
const LOCAL_PORT = config.localPort || 1337;
const REMOTE_PORT = config.remotePort || 7777;
const REMOTE_ADDR = config.remoteAddr || '10.25.2.17';
// 创建HTTP服务器
http.createServer(onRequest).listen(LOCAL_PORT);
log.info(`Gateway Server listening on port ${LOCAL_PORT}`);
function onRequest(req, res) {
if (req.url === '/favicon.ico') return;
log.info(`Request URL: ${req.url}`);
log.info(`Request Headers: ${JSON.stringify(req.headers)}`);
const options = {
hostname: REMOTE_ADDR,
port: REMOTE_PORT,
path: req.url,
method: req.method,
headers: req.headers
};
// 设置响应头
res.setHeader('Content-Type', 'application/json');
res.setHeader('Access-Control-Allow-Origin', '*');
const proxy = http.request(options, (chatRes) => {
log.info(`From ChatServer Status: ${chatRes.statusCode}`);
log.info(`From ChatServer Headers: ${JSON.stringify(chatRes.headers)}`);
chatRes.pipe(res, { end: true });
});
proxy.on('error', (e) => {
log.error(`Problem with request: ${e.message}`);
res.statusCode = 500;
res.end();
});
// 设置超时
proxy.setTimeout(10 * 1000, () => {
log.warn('Request Timeout, responding with 500');
res.statusCode = 500;
res.end();
});
req.pipe(proxy, { end: true });
req.setTimeout(10 * 1000, () => {
log.warn('Client request timeout, responding with 500');
res.statusCode = 500;
res.end();
});
}
关键点解释
- 配置日志:使用
log4js
模块配置日志输出,确保所有日志信息都被记录。 - 加载配置文件:使用
try-catch
语句确保配置文件能正确加载,避免程序因配置文件错误而崩溃。 - 设置响应头:确保响应头设置正确,以便客户端正确解析响应。
- 处理特殊字符:在客户端发送请求时,确保数据被正确编码,例如使用
encodeURIComponent()
对URL参数进行编码。 - 超时机制:为客户端请求和代理请求分别设置超时,确保服务器不会因为长时间无响应而挂起。
- 错误处理:详细记录错误信息并及时响应客户端。
以上改进应该能够帮助您更好地处理客户端请求和代理服务器之间的通信,避免因特殊字符和超时问题导致的服务挂起。
干嘛自己写,用这个https://github.com/nodejitsu/node-http-proxy
nodejitsu官方用的。自己写太麻烦了
好长,不看了。 不要用node做代理,用nginx。 node毕竟是js写逻辑,不如nginx的c语言更底层,而且很现成方便的东西,不用岂不是很可惜。 而且两个小时你就可以学会并搭建一个nginx代理服务器。
另外,即便node做服务器,要有一个文件专门写好路由映射规则,逻辑细节和规则分开会比较容易调试。
var proxy = function () { //封装异步请求,其余的你自己改试试,主要问题应该是没处理好node.js 的异步问题,好久没做Node.js开台开发,最近在研究JS本身。 http.request(options, function (res) { log.info('From ChatServer STATUS: ’ + res.statusCode); log.info('From ChatServer HEADERS: ’ + JSON.stringify(res.headers)); res.pipe(client_res, { end: true }); }); }; client_req.on(‘end’, proxy); 建议用责任链模式来设计,维护起来更方便 .
80行代码。。。。,关于用javascript 写逻辑,用高阶函数写逻辑还是挺爽的,没什么不好。
根据你的描述和代码,有几个可能的原因导致你的HTTP代理服务器会被挂起,并且无法响应新的请求:
- 未处理的数据流问题:在
client_req
和proxy
之间的管道连接中,如果一方的数据流没有正确结束,可能会导致死锁。 - 超时设置不当:虽然你已经设置了超时,但可能需要更灵活的超时机制来处理不同情况。
- 错误处理不完善:尽管你处理了一些错误情况,但可能还有一些未捕获的异常会导致程序挂起。
示例改进代码
以下是一些改进后的代码片段,可以帮助解决上述问题:
const http = require('http');
const log4js = require('log4js');
log4js.configure({
appenders: [
{ type: 'console' },
{ type: 'dateFile', filename: 'info.log', pattern: '-yyyy-MM-dd', alwaysIncludePattern: false, category: 'dateFileLog' }
],
replaceConsole: true,
levels: {
dateFileLog: 'INFO'
}
});
const log = log4js.getLogger('dateFileLog');
http.createServer(onRequest).listen(LOCAL_PORT);
log.info('Gateway Server listening on port ' + LOCAL_PORT);
function onRequest(client_req, client_res) {
if (client_req.url.indexOf('/favicon.ico') === 0) return;
log.info('Request URL: ' + client_req.url);
log.info('Request Headers: ' + JSON.stringify(client_req.headers));
const options = {
hostname: REMOTE_ADDR,
port: REMOTE_PORT,
path: client_req.url,
method: client_req.method,
headers: client_req.headers
};
client_res.setHeader('Content-Type', 'application/json');
client_res.setHeader('Access-Control-Allow-Origin', '*');
const proxy = http.request(options, function (res) {
log.info('From ChatServer STATUS: ' + res.statusCode);
log.info('From ChatServer HEADERS: ' + JSON.stringify(res.headers));
res.pipe(client_res, { end: true });
});
proxy.on('error', function (e) {
log.info('Problem with request from ChatServer: ' + e.message);
client_res.writeHead(500, { 'Content-Type': 'text/plain' });
client_res.end('Internal Server Error: ' + e.message);
});
proxy.setTimeout(10 * 1000, function () {
log.info('Request Timeout From ChatServer in 10 seconds, responding with status 500');
client_res.writeHead(500, { 'Content-Type': 'text/plain' });
client_res.end('Request Timeout');
});
client_req.pipe(proxy, { end: true });
client_req.setTimeout(10 * 1000, function () {
log.info('Timeout');
client_res.writeHead(500, { 'Content-Type': 'text/plain' });
client_res.end('Client Request Timeout');
});
client_req.pipe(proxy, { end: true });
}
process.on('uncaughtException', function (err) {
log.error('Caught exception: ' + err);
});
关键改进点
- 确保数据流正确关闭:通过在
res
和client_res
之间正确地使用pipe
方法来处理数据流。 - 增强错误处理:确保所有可能的错误都被捕捉并妥善处理。
- 统一状态码和响应格式:使用统一的状态码和响应格式来提高可维护性。
这些改进应该能帮助你的代理服务器更稳定地运行。如果问题仍然存在,可以进一步检查网络连接、日志记录以及具体的错误信息。