前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >HTTP请求的ECONNRESET

HTTP请求的ECONNRESET

原创
作者头像
sheazhang
修改2020-05-06 15:23:12
20.2K0
修改2020-05-06 15:23:12
举报
文章被收录于专栏:加速视频

概述

1、案例分享;

2、原理汇总;

3、解决方式;

4、总结;


一:案例分享

1、背景

某部分客户业务使用cos的node.js的sdk来进行上传下载等操作,近期客户端偶尔触发上传文件报错{ error: { code: 'ECONNRESET' } } 的异常。

那么此问题的到底从何而来,又该如何解决?

经了解:

业务的使用分块上传对象,且通过onProgress查看上传文件的进度回调函数;

代码语言:javascript
复制
const COS = require('cos-nodejs-sdk-v5');
let cos = new COS({
      getAuthorization: function (options, callback) {
          callback({
              TmpSecretId: cosconfig.obj.credentials.tmpSecretId,
              TmpSecretKey: cosconfig.obj.credentials.tmpSecretKey, 
              XCosSecurityToken: cosconfig.obj.credentials.stsToken,
              ExpiredTime: cosconfig.obj.expiredTime       
          })
      }
  })
​
cos.sliceUploadFile({
          Region       : cosconfig.obj.region,
          Bucket       : cosconfig.obj.bucket,
          Key               : destPath,
          FilePath: filepath,
          onProgress: cosprogress
      }, coscallback)  

2、排查:

1、requestid以及文件具体url信息查看对应cos的上传历史记录;

  • 发现server返回状态吗408;即:客户端建联后长时间没有传输数据,导致链接保持保持超时60s后触发了断开
  • http状态码参考

2、但是通过上传的onProgress来看进程是从0%到1%有发送数据的,并非长时间等待;

3、通过过滤多次上传log以及咨询确认我们nodejs的保持长连接的keep alive的特性;

服务端在60s内保持tcp的连接通路,此阶段没有发送数据,就会reset断掉连接,但是客户端在收到断开的tcp信息前,发起了http的新请求,导致服务端拒绝了请求;

3、原因:

总结一下就是:

在长连接的前提下,服务端先于客户端关闭了 TCP,而客户端此时还未同步状态,所以存在一个错误的暂态(客户端认为 TCP 连接依然在,但实际已经销毁了)

4、措施:

客户业务形态侧,可根据sdk中的参数自行调整。根据业务量级场景来评估调整:

  1. sdk 为了在发请求时共用 tcp 链接,减少频繁建立连接的消耗,所以默认 KeepAlive 是 true。如果上传并发量不大,且为了完全规避掉这类问题的话:可以考虑关闭该选项 new COS({ KeepAlive: false });
  2. 第一种方法的虽然可以完全规避,但是针对大量级业务下和高性能即:复用连接保持连接的性能下:所以
    1. 由于连接问题,发送失败,可以通过重试解决:调整sdk参数的分片重试次数,可以改大一些,比如 new COS({ ChunkRetryTimes: 10 });

二:原理详解

1、问题产生的源头是哪呢:

这里涉及到状态机制里竞争形态:

  1. 客户端与服务端建立长连接保持持久通道;
  2. tcp连接通道静默一段时间,期间并无 HTTP数据包的请求传输;
  3. 服务端因为在一段特定时间内没有收到任何数据,主动进行关闭了 TCP 连接;
  4. 客户端在收到 TCP 关闭的信息前,又开始主动发送了一个新的 HTTP 请求报文,需要进一步的传输数据;
  5. 服务端收到请求后直接进行了拒绝,客户端报错 ECONNRESET

总结一下就是:

服务端先于客户端关闭了 TCP,而客户端此时还未同步状态,所以存在一个错误的暂态(客户端认为 TCP 连接依然在,但实际已经销毁了)

2、对应的链路图:

正常的tcp的连接和keep alive

因为静默导致超时断开连接的状态


三:目前解决的方式

既然问题的原理和源头来源清楚了,那也就需要从根本上来进行规避掉;

但是由于长连接一系列保持的长期会话的特性和优点,使得我们不得不去借用,所以我们或许可采用的是去利用现状的bug特性,而不是去完全避开它;

方式一:

彻底的去避开它,直接使用短链接,即keep-alive的false关闭掉;

  • 优点是: 根源上杜绝了此类问题的产生,不会在产生客户端的此类报错;
  • 缺点是: 长连接变为了短链接,业务形态和处理性能自然不同。
方式二:

客户端先于服务端关闭 TCP 连接

把客户端的 keep-alive 超时时间设置得短一些 < 短于服务端的超时时间;

这样就可以保证永远是客户端这边超时关闭的 TCP 连接,消除了错误的暂态。

优点:

可以较大程度的既解决规避了这个问题,又保持了keep alive长连接的业务特性。

缺点:

实际生产环境中却是没法 100% 解决;

因为即使把客户端超时时间缩短到一定的数值,因为中间公网路由的网络延迟的存在,始终无法保证所有的

客户端的 keep-alive 超时时间 + 网络延迟的时间 都小于服务端设置的值;

如果把客户端超时时间设置得太小,又失去了keep alive的长连接保持的意义。

具体配置方法可参考:

服务端在response header中告诉客户端的头部的超时时间的信息

可参考:https://zhuanlan.zhihu.com/p/34147188

方式三:

重传重试

在识别到对应错误码后,且是复用了同样的tcp连接,现在最新的node.js已经可以通过req.reusedSocket来识别到是否复用了同一个连接。

优点:

在之前两种方法的基础上,可以最大程度的确保业务的正确性,重试解决现网存在的此类所有的报错问题;

缺点:

重试消耗少部分性能;


四:针对浏览器的优雅处理方式

作为经典的cs架构请求方式,浏览器自然也会遇到这个问题,但是我们却很少发现有浏览器会报错408的错误状态码;

由于浏览器为了规避此类问题,直接采用了感官无法感知到的优雅处理方式:

直接进行重试自动重试新连接中的其余请求,以便用户完全不知道发生的潜在故障

可参考:https://stackoverflow.com/questions/42631273/how-do-browsers-handle-http-keepalive-race-condition

后续可参考以上方法方式解决此类报错。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 概述
  • 一:案例分享
  • 二:原理详解
    • 1、问题产生的源头是哪呢:
      • 2、对应的链路图:
      • 三:目前解决的方式
        • 方式一:
          • 方式二:
            • 方式三:
            • 四:针对浏览器的优雅处理方式
            相关产品与服务
            对象存储
            对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档