举个栗子

好读书,不求甚解

使用node.js开发proxy程序

Kevalin's Avatar 2017-01-11

这是一个工作中的遗留问题。

对_http_client.js、_http_server.js、stream.js源码的简单研究

之前使用node.js写了一个proxy程序,完成http请求的转发任务,在生产环境中一直存在hang up的问题,proxy大概的工作原理如下:

大致流程如下:

  1. client发送请求到proxy
  2. proxy收到请求后pipe到site
    1
    req.pipe(preq)
  3. site收到proxy的请求处理之后返回给proxy
  4. proxy将结果pipe给client,完成代理工作
    1
    pres.pipe(res)

    核心点分析:

  5. req是一个http.inComingMessage可读流实例
  6. preq是通过http.ClientRequest类得到的一个可写流实例
  7. pres其实是preq.on(‘response’)得到的一个http.inComingMessage的可读流实例
  8. res是一个http.ServerResponse的可写流实例

问题:

如果client发送请求到proxy,proxy发送请求到site,site还没有来得及response给client的时候,如果client.abort()(也就是client到proxy的socket已经close),proxy会如何处理?

答:此时proxy还是会正常处理site的返回,但是再将site的返回 pipe到client的时候发现socket已经close,这个时候会触发req的aborted事件,监控到req的aborted事件之后应该close掉proxy到site的socket,这样处理之后会更好的释放socket连接,节省系统资源。还有一个要点就是流的pipe结束会自动结束socket连接(详情可以查看stream.js的源码)。

涉及到的源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
// _http_server.js
function abortIncoming() {
while (incoming.length) {
var req = incoming.shift();
req.emit('aborted');
req.emit('close');
}
// abort socket._httpMessage ?
}

// _http_client.js
function socketCloseListener() {
var socket = this;
var req = socket._httpMessage;
debug('HTTP socket close');

// Pull through final chunk, if anything is buffered.
// the ondata function will handle it properly, and this
// is a no-op if no final chunk remains.
socket.read();

// NOTE: It's important to get parser here, because it could be freed by
// the `socketOnData`.
var parser = socket.parser;
req.emit('close');
if (req.res && req.res.readable) {
// Socket closed before we emitted 'end' below.
req.res.emit('aborted');
var res = req.res;
res.on('end', function() {
res.emit('close');
});
res.push(null);
} else if (!req.res && !req.socket._hadError) {
// This socket error fired before we started to
// receive a response. The error needs to
// fire on the request.
req.emit('error', createHangUpError());
req.socket._hadError = true;
}

// Too bad. That output wasn't getting written.
// This is pretty terrible that it doesn't raise an error.
// Fixed better in v0.10
if (req.output)
req.output.length = 0;
if (req.outputEncodings)
req.outputEncodings.length = 0;

if (parser) {
parser.finish();
freeParser(parser, req, socket);
}
}

本文作者 : Kevalin
本文使用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 协议
本文链接 : https://kevalin.github.io/2017/01/11/%E4%BD%BF%E7%94%A8node-js%E5%BC%80%E5%8F%91proxy%E7%A8%8B%E5%BA%8F/