上传总在七八成的时候失败,问题多半不在客户端
用户反馈上传到一大半就断了,重试还是断在差不多的地方。这种有规律的失败,通常不是网络偶然抖动,而是链路上某一道限制在起作用,而且往往不在客户端。一、失败位置说明什么
每次都断在差不多的百分比,说明失败跟数据量有关,跟时间关系不大。数据量到了某个阈值就出问题,这个阈值就是突破口。把失败时的字节数记下来,几次一对,阈值就出来了,比猜快得多。临时文件要有人清,传到一半断掉留下的碎片,攒上几个月能把磁盘占满。
如果断点位置每次都不一样,更像是网络或超时的问题;这时候要看失败前的速率,速率突然掉到零再断开,多半是链路那边先出了问题。如果固定在某个大小附近,就要去查各层的限制:请求体大小、临时目录空间、网关的超时时间。还有一个容易被忽略的是连接空闲超时,传着传着停了几十秒,中间设备认为这条连接没用了,直接回收。高峰期可以把非关键的上传排到后面,先顾住主要业务的响应。
二、网关和代理的限制
反向代理一般有请求体大小上限,也有读写超时。文件传到一半,代理等不到后续数据,或者累计时间超过阈值,就会把连接断掉,客户端看到的就是失败。分片的大小要按网络状况调,片太小请求次数多,片太大失败一次重传的成本高。
这类限制要一层层查:浏览器到网关、网关到应用、应用到存储。每一层都可能有自己的数字,改了一层忘了另一层,问题还会出现。查的时候从外面那一层往里走,先看靠近用户的那个入口,再逐层往里,比一上来就翻应用代码快。传输协议的版本要统一,新旧混用的时候,问题往往出在兼容上。
三、服务端的临时空间
上传过程中文件通常先落在临时目录,传完再转移到正式位置。两者的区别在服务端日志,磁盘写满会有明确的报错,网络中断往往什么也留不下。临时目录所在的分区如果快满了,写到某个大小就会失败,表现和网络中断很像。
这类问题在磁盘用得多、又没有监控的机器上很常见。要单独监控临时目录的使用率,别等整个磁盘告警了才发现。临时目录尽量单独挂一块盘,跟系统盘分开,这样即使写满了,也不会把日志和系统文件一起拖下水。容量规划要按增量算,每天新增多少、保留多久,乘出来才是真实需求。
四、内存也可能是原因
有些框架默认把上传内容读进内存再处理,文件一大就吃光堆内存,进程被系统杀掉,连接自然就断了。看监控里的内存曲线比较直接,上传一开始就往上冲,基本可以确定是这个问题。日志里往往只留一条连接重置,看不出真正原因。
解决的办法是流式处理,边收边写,不把整个文件留在内存里。分片上传本身就缓解了这个问题,每一片都很小。流式处理要注意关闭的顺序,异常的时候没关掉句柄,跑上几天就会把文件句柄耗光。上传下载这条链路长,任何一段出问题都表现为慢,分段计时是比较省事的排查办法。
五、日志要能定位
失败的时候,客户端和服务端都要留下足够的信息:文件大小、已传字节数、请求编号、错误码。只有一句上传失败,排查只能靠猜。
请求编号尤其重要。用户报障时给出编号,服务端一搜就能找到那次请求的全部记录,比问用户传的什么文件快得多。编号要短、要好念,用户打电话报障的时候能说清楚,太长的一串数字,念错一位就查不到。上传的接口要单独部署,跟普通接口混在一起,一个大文件就能把整个服务拖慢。
六、怎么复现
用同样大小的文件在测试环境复现,逐步调整各层的限制,看在哪一层开始失败。能稳定复现,问题就解决了一半。
复现的时候别忘了模拟弱网。本地环境太快,很多超时问题根本不会出现,用工具限一下带宽和延迟,才接近真实情况。限速工具要同时限上行和延迟,只限其中一个,很多问题还是复现不出来。文件大小要在入口处就挡住,等传完了才发现超限,前面的流量全白费。
上传失败这件事,用户看到的是一次意外,背后往往是几个配置数字凑在一起。把这些数字记下来,下次换环境就不用重新踩一遍。 对这个话题有了更全面的认识,不再只停留在表面理解。 这个话题确实很值得讨论,我也有类似的经历,非常认同楼主的观点。 观点很成熟,分析也很理性,值得大家静下心来认真看一看。 对新手特别友好,解释得很细致,就算零基础也能看懂大半。
页:
[1]