在过去,request 模块几乎是 Node.js 端的不二选择,可惜已被放弃维护。如今流行的模块虽然变多,但不意味着它们足够成熟,我还是倾向于专注 Node.js 端的那几个。
需求越简单,选择越不重要。不过相较而言,Got 的接口设计看起来更友好,并且它是做到支持 Connection Timeout 和 Read Timeout 的少数。
见 https://github.com/sindresorhus/got/#comparison 的 Advanced timeouts
实际用下来,还是遇到了坑,顺便扒了一眼 Got 的代码。
native http?
先解释一下为什么不用原生 http 模块吧。
毕竟在 Node.js 提供代理服务是非常容易的事儿,普遍的优化无非是:
- 使用 http.Agent。用它来支持 keepAlive,关闭 Nagle 算法等等。
- 使用 stream.pipe()。相对于“接收-等待-发送”的模式,一方面主要是节省内存,另一方面可以减少等待,提高传输效率(但也不是绝对的)。
不用安装第三方依赖,基于原生 http 写上十来行代码即可完工,所以我自己很喜欢直接用 http 做一些工作。
但如果代理需求变得复杂,使用现成的轮子才能利于队友们(也包括自己)维护。
decompress
Got 默认对响应执行 decompress,对于代理而言毫无意义,需要关掉。
1 | async _onResponseBase(response: IncomingMessageWithTimings): Promise<void> { |
这在 decompress 的文档上有说明,但是一点儿都不醒目。
If this is disabled, a compressed response is returned as a Buffer. This may be useful if you want to handle decompression yourself or stream the raw compressed data.
最坑的是,我是因为遇到了 stream 的 bug (#issues/1279) 才注意到这个选项。在开启 decompress 的情况下,使响应值的 content-length 是错误的,该 bug 会导致返回的结果不完整。
accept-encoding
关闭 decompress 之后,响应开始变慢到肉眼可见得慢。
原因如下:
1 | async _makeRequest(): Promise<void> { |
accept-encoding 只在 decompress 为 true 的时候设置,否则无法享用 gzip 等压缩带来的优化。
solution
1 | const got = require('got'); |
这大概率不是最终版,立一个 flag,如果再遇到问题,就自己从头写一个专用于代理场景的 http 库。