网络代理实现
网络代理的基本概念
网络代理是一种中间服务器,充当客户端和目标服务器之间的中介。客户端向代理发送请求,代理再将请求转发给目标服务器,并将响应返回给客户端。这种机制可以实现多种功能,如匿名访问、内容过滤、负载均衡等。在Node.js中,我们可以利用其强大的网络模块轻松实现各种代理功能。
代理主要分为正向代理和反向代理两种类型。正向代理代表客户端向服务器发送请求,常用于突破网络限制或保护客户端隐私;反向代理则代表服务器接收客户端请求,常用于负载均衡或缓存静态内容。
Node.js中的HTTP代理实现
使用Node.js创建基本的HTTP代理非常简单。以下是一个最简单的HTTP代理实现示例:
const http = require('http');
const httpProxy = require('http-proxy');
const proxy = httpProxy.createProxyServer({});
http.createServer((req, res) => {
proxy.web(req, res, { target: 'http://example.com' });
}).listen(8080);
console.log('Proxy server running on port 8080');
这个例子创建了一个将所有请求转发到example.com的代理服务器。http-proxy是一个流行的Node.js代理库,提供了丰富的功能。
处理HTTPS请求
处理HTTPS请求需要额外配置SSL证书。以下是处理HTTPS流量的代理实现:
const https = require('https');
const fs = require('fs');
const httpProxy = require('http-proxy');
const proxy = httpProxy.createProxyServer({
ssl: {
key: fs.readFileSync('key.pem', 'utf8'),
cert: fs.readFileSync('cert.pem', 'utf8')
},
secure: false // 忽略目标服务器的证书验证
});
https.createServer({
key: fs.readFileSync('key.pem'),
cert: fs.readFileSync('cert.pem')
}, (req, res) => {
proxy.web(req, res, { target: 'https://example.com' });
}).listen(443);
console.log('HTTPS proxy running on port 443');
中间件和请求修改
在实际应用中,我们经常需要在代理过程中修改请求或响应。http-proxy库提供了事件钩子来实现这一功能:
const httpProxy = require('http-proxy');
const proxy = httpProxy.createProxyServer({});
// 修改请求头
proxy.on('proxyReq', (proxyReq, req, res, options) => {
proxyReq.setHeader('X-Proxy-Header', 'Node-Proxy');
if (req.body) {
const bodyData = JSON.stringify(req.body);
proxyReq.setHeader('Content-Type', 'application/json');
proxyReq.setHeader('Content-Length', Buffer.byteLength(bodyData));
proxyReq.write(bodyData);
}
});
// 修改响应
proxy.on('proxyRes', (proxyRes, req, res) => {
proxyRes.headers['X-Proxy-Response'] = 'Modified';
});
http.createServer((req, res) => {
proxy.web(req, res, { target: 'http://example.com' });
}).listen(8080);
WebSocket代理
现代应用经常使用WebSocket进行实时通信。Node.js代理也可以处理WebSocket连接:
const http = require('http');
const httpProxy = require('http-proxy');
const proxy = httpProxy.createProxyServer({
target: 'ws://echo.websocket.org',
ws: true
});
const server = http.createServer((req, res) => {
proxy.web(req, res, { target: 'http://example.com' });
});
server.on('upgrade', (req, socket, head) => {
proxy.ws(req, socket, head);
});
server.listen(8080);
负载均衡实现
代理服务器常用于实现负载均衡。以下是一个简单的轮询负载均衡实现:
const http = require('http');
const httpProxy = require('http-proxy');
const servers = [
{ host: 'server1.example.com', port: 80 },
{ host: 'server2.example.com', port: 80 },
{ host: 'server3.example.com', port: 80 }
];
let current = 0;
const proxy = httpProxy.createProxyServer();
http.createServer((req, res) => {
const target = servers[current];
current = (current + 1) % servers.length;
proxy.web(req, res, { target: `http://${target.host}:${target.port}` });
}).listen(8080);
缓存机制实现
为了提高性能,可以在代理中实现缓存机制:
const http = require('http');
const httpProxy = require('http-proxy');
const NodeCache = require('node-cache');
const cache = new NodeCache({ stdTTL: 60 }); // 缓存60秒
const proxy = httpProxy.createProxyServer();
http.createServer((req, res) => {
const cacheKey = req.url;
const cached = cache.get(cacheKey);
if (cached) {
res.writeHead(200, { 'Content-Type': 'text/html' });
return res.end(cached);
}
proxy.web(req, res, {
target: 'http://example.com'
}, (err) => {
if (err) {
res.writeHead(500);
res.end('Proxy error');
}
});
proxy.on('proxyRes', (proxyRes, req, res) => {
let body = [];
proxyRes.on('data', (chunk) => {
body.push(chunk);
});
proxyRes.on('end', () => {
body = Buffer.concat(body).toString();
cache.set(cacheKey, body);
});
});
}).listen(8080);
身份验证和访问控制
为代理添加基本身份验证:
const http = require('http');
const httpProxy = require('http-proxy');
const auth = require('basic-auth');
const proxy = httpProxy.createProxyServer();
http.createServer((req, res) => {
const credentials = auth(req);
if (!credentials || credentials.name !== 'admin' || credentials.pass !== 'password') {
res.writeHead(401, {
'WWW-Authenticate': 'Basic realm="Proxy Authentication Required"'
});
return res.end('Access denied');
}
proxy.web(req, res, { target: 'http://example.com' });
}).listen(8080);
性能优化技巧
优化代理服务器性能的几个关键点:
- 连接池管理:重用与目标服务器的连接
- 压缩传输:启用gzip压缩减少带宽使用
- 智能缓存:根据内容类型设置不同的缓存策略
const httpProxy = require('http-proxy');
const zlib = require('zlib');
const proxy = httpProxy.createProxyServer({
agent: new http.Agent({ keepAlive: true, maxSockets: 100 })
});
proxy.on('proxyRes', (proxyRes, req, res) => {
const encoding = proxyRes.headers['content-encoding'];
if (encoding === 'gzip') {
delete proxyRes.headers['content-encoding'];
proxyRes.pipe(zlib.createGunzip()).pipe(res);
} else {
proxyRes.pipe(res);
}
});
错误处理和日志记录
完善的错误处理和日志记录对于生产环境代理至关重要:
const http = require('http');
const httpProxy = require('http-proxy');
const fs = require('fs');
const morgan = require('morgan');
const accessLogStream = fs.createWriteStream('proxy-access.log', { flags: 'a' });
const proxy = httpProxy.createProxyServer();
proxy.on('error', (err, req, res) => {
console.error('Proxy error:', err);
res.writeHead(500, { 'Content-Type': 'text/plain' });
res.end('Proxy server error');
});
http.createServer(morgan('combined', { stream: accessLogStream }), (req, res) => {
proxy.web(req, res, { target: 'http://example.com' });
}).listen(8080);
高级路由配置
根据不同的URL路径将请求路由到不同的后端服务器:
const http = require('http');
const httpProxy = require('http-proxy');
const url = require('url');
const proxy = httpProxy.createProxyServer();
http.createServer((req, res) => {
const path = url.parse(req.url).pathname;
if (path.startsWith('/api')) {
proxy.web(req, res, { target: 'http://api-server.example.com' });
} else if (path.startsWith('/static')) {
proxy.web(req, res, { target: 'http://static-server.example.com' });
} else {
proxy.web(req, res, { target: 'http://web-server.example.com' });
}
}).listen(8080);
容器化部署
将Node.js代理服务容器化以便于部署:
FROM node:14
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 8080
CMD ["node", "proxy.js"]
然后可以使用Docker Compose编排多个服务:
version: '3'
services:
proxy:
build: .
ports:
- "8080:8080"
restart: always
api-server:
image: api-server-image
static-server:
image: static-server-image
安全注意事项
实现代理服务器时需要考虑的安全问题:
- 防止请求走私攻击
- 限制请求头大小
- 验证目标URL防止SSRF攻击
- 实施速率限制防止滥用
const http = require('http');
const httpProxy = require('http-proxy');
const rateLimit = require('express-rate-limit');
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15分钟
max: 100 // 每个IP限制100个请求
});
const proxy = httpProxy.createProxyServer();
http.createServer((req, res) => {
// 验证目标URL
const target = req.headers['x-target'];
if (!isValidTarget(target)) {
res.writeHead(400);
return res.end('Invalid target URL');
}
// 应用速率限制
limiter(req, res, () => {
proxy.web(req, res, { target: target });
});
}).listen(8080);
function isValidTarget(url) {
// 实现URL验证逻辑
return url.startsWith('http://allowed-domain.com');
}
实际应用场景
网络代理在实际开发中有多种应用场景:
- 开发环境跨域请求代理
- 企业内网访问外部资源的统一出口
- 微服务架构中的API网关
- 内容分发网络的边缘节点
- 爬虫系统的IP轮换机制
// 开发环境跨域代理配置示例
const proxy = require('http-proxy-middleware');
module.exports = function(app) {
app.use(
'/api',
proxy({
target: 'http://backend.example.com',
changeOrigin: true,
pathRewrite: { '^/api': '' }
})
);
};
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
上一篇:WebSocket实现
下一篇:DNS解析