调用server.close()后未处理HTTP请求的相关技术咨询
Node.js服务器关闭后HTTP请求残留问题解答
问题场景
我搭建了一个原生Node.js服务器,按下ctrl-c后,会看到"Server is closed"日志,随后出现命令行提示符,但之后仍会有日志显示还在发起API调用。
在exit()中(server.close()之前)调用clearInterval(anInterval)后,服务器关闭后仍运行定时器的问题已解决,但根据Node.js官方文档说明:
关闭所有未发送请求或等待响应的服务器连接。
我推测server.close()不会自动终止HTTP请求,因此有以下疑问:
- 当计算机/Node不再追踪变量
someVar时,HTTP响应信息会如何处理? - 不专门终止发起HTTP请求并等待响应的线程会有什么后果?
- 取消HTTP请求的最佳实践是什么?
- 具体实现是通知API服务器停止发送响应,还是仅让Node不再接收新信息?
服务器代码
let someVar // to be set to a Promise const getData = url => { return new Promise((resolve, reject) => { https.get( url, { headers: { ...COMMON_REQUEST_HEADERS, 'X-Request-Time': '' + Date.now() } }, res => { if (res.statusCode === 401) return reject(new RequestError(INVALID_KEY, res)) if (res.statusCode !== 200) return reject(new RequestError(BAD_REQUEST, res)) let json = '' res.on('data', chunk => json += chunk) res.on('end', () => { try { resolve(JSON.parse(json).data) } catch (error) { return reject(new RequestError(INVALID_RESPONSE, res, json)) } }) } ).on('error', error => reject(new RequestError(FAILED, error))) }) } const aCallback = () => { console.log('making api call') someVar = getData('someApiEndpoint') .then(data => { ... }) } const main = () => { const server = http.createServer(handleRequest) anInterval = setInterval(aCallback, SOME_LENGTH_OF_TIME) const exit = () => { server.close(() => process.exit()) log('Server is closed') } process.on('SIGINT', exit) process.on('SIGTERM', exit) process.on('uncaughtException', (err, origin) => { log(`Process caught unhandled exception ${err} ${origin}`, 'ERROR') }) } main()
问题解答
1. 当计算机/Node不再追踪变量someVar时,HTTP响应信息会如何处理?
Node.js的HTTP请求基于TCP连接,只要TCP连接未断开,即便你不再持有someVar这个Promise的引用,事件循环仍会监听该连接的响应数据。如果响应返回时,Promise的回调(then/catch)还因闭包存在于内存中,回调依然会执行;若回调已被垃圾回收,响应数据会被内核接收后丢弃,但TCP连接可能会按Keep-Alive规则保持一段时间,直到超时关闭。
2. 不专门终止发起HTTP请求并等待响应的线程会有什么后果?
Node.js是单线程事件循环模型,不存在专门的请求线程——所有请求都在事件循环中处理。不终止未完成的HTTP请求,会占用事件循环资源,可能导致进程无法正常退出(除非强制调用process.exit())。此外,挂起的TCP连接会占用系统文件描述符,数量过多会导致新请求无法建立;同时API服务器会继续发送响应,浪费网络带宽和服务器资源。
3. 取消HTTP请求的最佳实践是什么?
- 调用请求对象的
destroy()方法:https.get()返回的客户端请求对象调用destroy()可立即终止TCP连接,触发请求的error事件,对应的Promise会被reject。 - 维护未完成请求列表:用数组追踪所有正在进行的请求对象,在退出逻辑中遍历数组逐个调用
destroy()。 - 使用
AbortController(Node.js v15+支持):通过信号取消请求,符合现代异步API设计,还能同时取消多个关联请求。
示例实现(基于destroy()):
// 修改getData函数,返回请求对象和Promise const getData = url => { const promise = new Promise((resolve, reject) => { const req = https.get( url, { headers: { ...COMMON_REQUEST_HEADERS, 'X-Request-Time': '' + Date.now() } }, res => { if (res.statusCode === 401) return reject(new RequestError(INVALID_KEY, res)) if (res.statusCode !== 200) return reject(new RequestError(BAD_REQUEST, res)) let json = '' res.on('data', chunk => json += chunk) res.on('end', () => { try { resolve(JSON.parse(json).data) } catch (error) { return reject(new RequestError(INVALID_RESPONSE, res, json)) } }) } ).on('error', error => reject(new RequestError(FAILED, error))) // 将req对象暴露出去 req.promise = promise return req }) } // 维护请求列表 const pendingRequests = [] const aCallback = () => { console.log('making api call') const req = getData('someApiEndpoint') pendingRequests.push(req) someVar = req.promise .then(data => { ... }) .finally(() => { // 请求完成后从列表移除 const index = pendingRequests.indexOf(req) if (index !== -1) pendingRequests.splice(index, 1) }) } // 更新exit函数 const exit = () => { clearInterval(anInterval) // 取消所有未完成的请求 pendingRequests.forEach(req => req.destroy()) server.close(() => process.exit()) log('Server is closed') }
4. 具体实现是通知API服务器停止发送响应,还是仅让Node不再接收新信息?
- 主动调用
destroy()或通过AbortController取消请求时,Node.js会发送TCP FIN包关闭连接。API服务器收到FIN包后,会知晓客户端不再接收数据,通常会停止发送剩余响应(若还在发送中),并关闭自身侧的连接。 - 若仅丢弃Promise引用而不主动取消,Node.js不会通知API服务器,服务器会继续发送完整响应,直到数据全部发送完毕,之后连接会按Keep-Alive规则关闭或直接断开。
内容的提问来源于stack exchange,提问作者Aaron Parisi
相关产品推荐
相关产品推荐

