![Docker 竞争条件导致 curl 错误“传输已关闭,剩余 1 个字节需要读取”](https://linux22.com/image/748877/Docker%20%E7%AB%9E%E4%BA%89%E6%9D%A1%E4%BB%B6%E5%AF%BC%E8%87%B4%20curl%20%E9%94%99%E8%AF%AF%E2%80%9C%E4%BC%A0%E8%BE%93%E5%B7%B2%E5%85%B3%E9%97%AD%EF%BC%8C%E5%89%A9%E4%BD%99%201%20%E4%B8%AA%E5%AD%97%E8%8A%82%E9%9C%80%E8%A6%81%E8%AF%BB%E5%8F%96%E2%80%9D.png)
很难完整描述我们的问题。不过,我会尽力提供完整的信息。我花了很多时间,但仍然卡住了。希望这里的任何人都可以帮助我。
目前,我们的容器在响应网络请求时随机挂起。
我有一个在端口 8082 上运行的 nodejs 应用程序,此端口也暴露给主机。我创建了一个桥接网络以将其与 nginx 代理容器链接起来。
我写了一个脚本来执行以下操作
- 停止 nodejs 容器
- 运行一个新的 nodejs 容器(来自相同或更新的图像)
- 删除网络
- 停止 nginx 容器
- 创建新网络
- 将 nodejs 链接到新网络
- 使用新网络运行另一个 nginx 容器
有时,这种设计很完美。但有时它却被破坏了随机。中断发生在容器启动后,一旦中断,所有请求都会中断。
以下是它被破坏的方式
- 从主机上,如果我运行
curl localhost:8082
,它会返回Found. Redirecting to /en
。这是正确的。 - 从主机上,如果我运行
curl localhost:8082/en
,它会挂起很长时间然后返回curl: (52) Empty reply from server
。 - 从主机,我运行
docker exec 041fe684edfb curl localhost:8082/en
(041fe684edfb 是容器名称),它返回 99% 的响应并在打印时挂起<script defer src="https://d3l80sdjn9d1ye.cloudfront.net/bundles/admin-car-calendar~admin-host~admin-orders~admin-station~admin-stations~author-postView~holder-car-c~b1a1af4b.05ea0cbf23227e7a323a.js"></script>
,然后返回curl: (18) transfer closed with 1 bytes remaining to read
。 - 如果我运行,然后在容器终端中
docker exec -it 041fe684edfb bash
运行,它会打印响应,挂起但在不同的(后者)位置curl localhost:8082/en
<script defer src="https://d3l80sdjn9d1ye.cloudfront.net/bundles/runtime.93923d84c7f89f7844ae.js"></script>
<script defer src="https://d3l80sdjn9d1ye.cloudfront.net/bundles/admin-car-calendar~admin-host~admin-orders~admin-station~admin-stations~author-postView~holder-car-c~b1a1af4b.05ea0cbf23227e7a323a.js"></script>
<script defer src="https://d3l80sdjn9d1ye.cloudfront.net/bundles/admin-car-calendar~admin-car-orders~admin-cars~holder-car-calendar~holder-car-simple-calendar~holder~9dc2a71c.5714564e356df216ebb8.js"></script>
<script defer src="https://d3l80sdjn9d1ye.cloudfront.net/bundles/user-vs-home.dc7ac409e63e95c891ec.js"></script>
<script defer src="https://d3l80sdjn9d1ye.cloudfront.net/bundles/vendors.284ffc3f329d46200747.js"></script>
<script defer src="https://d3l80sdjn9d1ye.cloudfront.net/bundles/client.16b9bdc0c6df3c59afb6.js"></script>
<script defer src="https://d3l80sdjn9d1ye.cloudfront.net/bundles/styles.91c5d85ab779777de531.js"></script>
</body>
</html>
沒有打印出来。
当我检查我的nodejs应用程序日志时,该请求已完全处理该请求(直到</html>
)。
更多信息
- 如果我使用重新启动容器
docker restart 041fe684edfb
,问题就会消失 - 如果我逐个运行脚本命令,问题就会消失
- 我的 nodejs 应用程序连接到 10.0.81.135(AWS EC2 私有 IP)上的外部 mongo 和 redis 服务器。最初,这些服务器在其他容器中本地运行,完全没有问题。在我将它们迁移到单独的服务器后,问题就出现了。
- 块大小为 221773。Nodejs 应用程序日志打印此大小并且 curl
Content-Length
正确解释标题。 - 最后,nodejs抛出这个错误几次(6次),然后退出
12:07:00
AbortError: Redis connection lost and command aborted. It might have been processed.
12:07:00
at RedisClient.flush_and_error (/carstay/node_modules/redis/index.js:362:23)
12:07:00
at RedisClient.connection_gone (/carstay/node_modules/redis/index.js:664:14)
12:07:00
at RedisClient.on_error (/carstay/node_modules/redis/index.js:410:10)
12:07:00
at Socket.<anonymous> (/carstay/node_modules/redis/index.js:279:14)
12:07:00
at Socket.emit (events.js:223:5)
12:07:00
at Socket.EventEmitter.emit (domain.js:475:20)
12:07:00
at emitErrorNT (internal/streams/destroy.js:92:8)
12:07:00
at emitErrorAndCloseNT (internal/streams/destroy.js:60:3)
12:07:00
at processTicksAndRejections (internal/process/task_queues.js:81:21)
12:07:00
AbortError: Redis connection lost and command aborted. It might have been processed.
12:07:00
at RedisClient.flush_and_error (/carstay/node_modules/redis/index.js:362:23)
12:07:00
at RedisClient.connection_gone (/carstay/node_modules/redis/index.js:664:14)
12:07:00
at RedisClient.on_error (/carstay/node_modules/redis/index.js:410:10)
12:07:00
at Socket.<anonymous> (/carstay/node_modules/redis/index.js:279:14)
12:07:00
at Socket.emit (events.js:223:5)
12:07:00
at Socket.EventEmitter.emit (domain.js:475:20)
12:07:00
at emitErrorNT (internal/streams/destroy.js:92:8)
12:07:00
at emitErrorAndCloseNT (internal/streams/destroy.js:60:3)
12:07:00
at processTicksAndRejections (internal/process/task_queues.js:81:21)
答案1
遇到了类似的问题并找到了这篇文章:https://medium.com/grano/the-fault-in-our-responses-bytes-remaining-to-read-91efe0553676
尽管响应已经发送,自定义错误处理程序仍会调用 next(),这会导致此问题,因为最后一个 next() 是 Express 的默认处理程序,它会立即断开套接字。与此同时,Redis 可能仍会尝试保存您的会话并终止。
尝试逐步检查代码,看看在特定情况下请求是否被多次处理,例如在某个时候抛出错误。即使 res.send() 也可能会抛出错误,例如如果您尝试发送无法序列化的数据。