前言
用于记录该项目主要功能的部分实现与后续在面试中被问到的问题
上传
什么是md5?
MD5消息摘要算法(MD5 Message-Digest Algorithm),一种被广泛使用的密码散列函数,可以产生出一个128位(16字节)的散列值(hash value),用于确保信息传输完整一致。
webworker计算md5
计算md5时间很久,这段时间怎么办?(不知道怎么答)
弄个加载动画(?
Rust可以优化(不会
大文件切片上传
前置流程
计算完md5后对后端发起post请求,询问文件是否上传过,此时可能会得到三种结果:
- 未上传
- 部分上传,分块上传逻辑
- 已上传,秒传逻辑
并行上传切片
上传并发数默认是3,上传切片大小默认是1M。从理论层面来说:
第一,Chrome 对于同一个域名的 http1.1 请求来说,最大支持的并发请求数是6,如果单请求在整个通路上有最大速度限制,那调大这个并发数就是有意义的;
第二,切片1MB,对于一个5000MB的视频来说就切了5000片,如果网速足够快的话 http 建立连接的时间可能都比传输速度长。
进一步思考:并发数如果调到6,就相当于这个域名下所有请求都被阻塞了,如果还有其他依赖这个域名的请求就会很慢影响用户体验。如果切片大小很大,那在差网络情况下,例如 200KB/s,切片大小32MB,用户好不容易传到10MB了因为网络问题失败了,那就要重试等待很久,也影响用户体验。
解决
- 首先进行埋点,在当时的情况尽可能多的获得用户的网速层面,来定义默认配置行为
- 在用户上传前检测网络情况,对于网络情况好的用户配置更高的切片大小
注意点
- 分片大小会影响续传,分片大小变了,下次就会续传不了。所以只设置了跨度较大的8M和32M两个大小
- 目前6个,会不会有其他请求需要用到该域名?预留一个机会给列出文件等请求操作,也就是设置5并发,等域名扩展再搞多并发
新思路
如果并发数量能够根据用户的实时网速灵活变化,当网速慢时并发数量低,网速快时并发数量高就能解决上述问题了
详细方案
我们可以从BBR算法和TCP的拥塞策略中借鉴思路
策略目标:根据用户实时网速动态控制并发数量保证上传成功率,并尽可能利用用户带宽 基于上述目标优化并发上传的策略:
慢启动
当刚刚开始上传时,不能一开始将并发数量(concurrency)设置得很大,这样如果用户网络状况差容易一开始就造成上传超时,我们在开始先将 concurrency =2,然后根据传输情况来逐步扩大concurrency来适应当前的网络状态,直到达到慢启动的门限阈值(ssthresh),步骤如下:
- 初始化设置
concurrency=2
,concurrency = 16
,并发传输两个分片 - 每成功上传一个包,就会将concurrency加1
- 当初始两个包上传成功后,即完成第一轮上传时concurrency为4
- 当
concurrency< ssthresh
且每个分片都成功上传时,concurrency按每轮concurrency *2指数级上升 - 当
concurrency >= ssthresh
或发生上传超时,则认为当前并发量超过了网络传输速度,发生了网络拥塞,慢启动结束,进入拥塞避免状态。
拥塞避免
当 concurrency >= ssthresh
即并发数大于等于阈值时,有可能还未到网络传输速度的上限,这个时候需要进一步通过一个缓慢的调节过程来进行适配。
- 当concurrency >= ssthresh时,进入拥塞避免状态,ssthresh初始值设置为16
- 每轮concurrency个分片都上传成功后,并发数量加1,即设置
concurrency=concurrency + 1
,一但发现丢包和超时重传,则进入拥塞处理状态
拥塞处理
当分片上传出现了超时失败时,会将失败的包塞到待上传队列的队尾,进入拥塞处理状态。
- 进入拥塞处理状态会将concurrency = concurrency * 0.75,也就是将当前并发数量减小25%,然后设置ssthresh = concurrency
- 降低concurrency的实现方式是,以每收到4个分片记为一组,前三个分片收到上传结果,每个分片会向上传队列里加进去1个待上传分片,收到第四个分片的上传结果后不向上传队列加待上传分片。
总结
通过这种策略实现了分片上传的并发数量根据用户的实时网速灵活变化。
- 网速慢时并发数量低:最低降低到了并发1个分片上传,即串行上传,计算需要的最低网速为2M*1/10s=0.2M/s,也就是说就算用户平均网速只有200kb/s也能将文件上传成功。
- 网速快时并发数量高:当用户网速快时,并发数能很快增长到16,并且会随上传轮次保持线型增长,充分的利用了用户的带宽。对于30M的文件上传速度比旧策略快40%,70M的文件平均比旧策略快60%。
高并发情况等待队列处理
秒传
网络条件很差怎么办?
调整分片大小
资源占用
用户本地的资源占用
1. 内存占用
- 直接上传(非分片):
- 上传整个大文件时,文件通常会一次性加载到内存缓冲区(Buffer),导致内存占用较高,尤其对于超大文件。
- 上传过程中,内存占用会保持稳定,但较高。
- 分片上传:
- 文件被分成多个小块(如5MB一片)逐块加载到内存,上传完一块后立即释放,再加载下一块。
- 内存占用较低且稳定,主要受分片大小影响。
- 浏览器或客户端缓存:
- 文件数据可能会临时存储在内存中用于重试或预处理,若没有高效的分片机制,可能增加内存占用。
2. CPU占用
- 文件处理:
- 如果需要对文件进行预处理(如加密、压缩、签名计算等),CPU占用会增加。
- 分片上传通常需要对每个分片进行单独处理,因此分片大小和算法复杂度直接影响CPU占用。
- 网络传输:
- 数据上传过程中,CPU用于打包、发送数据包以及处理协议开销(如TCP/IP),占用一般较低,但网络波动可能增加重传开销。
- 并发分片上传:
- 若启用了多线程或多请求并发上传,CPU占用会显著增加,但提高了上传速度。
服务器的资源占用
1. 内存占用
- 直接上传(非分片):
- 文件上传到服务器时,整个文件会暂存于内存缓冲区或临时存储区,直到写入硬盘或对象存储。
- 超大文件可能导致内存溢出,因此通常限制单次上传大小。
- 分片上传:
- 每次只接收一个分片并存储,内存占用与分片大小成正比。
- 对于并发分片上传,内存占用会叠加,取决于同时处理的分片数和单个分片大小。
- 缓存和队列:
- 如果服务器使用了缓冲队列(如Redis、消息队列),内存占用会随着上传速度和文件大小增加。
2. CPU占用
- 数据校验:
- 文件分片通常需要校验完整性(如MD5或SHA计算),增加CPU占用。
- 分片重组:
- 上传完成后,服务器可能需要将分片重新合并成完整文件,尤其是本地磁盘存储时,合并操作会显著增加CPU和磁盘IO占用。
- 加密/解密、压缩:
- 服务器端可能对上传数据进行加密、解密或压缩处理,CPU消耗取决于算法复杂度。
- 并发处理:
- 高并发情况下,多个用户同时上传文件会导致服务器CPU开销增加,用于处理多个请求、分片合并和网络通信。
典型变化趋势
- 直接上传:
- 本地:内存占用随文件大小而增加,CPU占用中等偏低(除非有复杂的预处理操作)。
- 服务器:内存占用较高,CPU主要用于接收数据和校验完整性。
- 分片上传:
- 本地:内存占用与分片大小成正比,CPU占用较为平稳。
- 服务器:内存占用较低但随分片数和并发量增加,CPU在合并和校验阶段会有高峰。
- 弱网环境:
- 本地:由于重传机制,内存和CPU占用可能有波动。
- 服务器:频繁重试增加网络I/O和CPU开销。
优化措施
- 本地优化:
- 使用分片上传减少内存占用。
- 降低分片大小,缓解内存压力。
- 降低并发分片数,避免过高CPU使用。
- 服务器优化:
- 配置上传限流和分片大小,避免内存溢出。
- 增加异步处理队列,分担CPU和内存压力。
- 使用云存储(如S3)支持直接上传,减轻服务器压力。
- 优化分片合并逻辑,减少合并过程中的内存和CPU占用。
自定义Hooks
Antd组件二次封装
样式
由于有UI给出的确立好的设计稿,表单、列表等组件样式完全一致。可以直接在全局中使用id选择器加强权重的方式来避免 !impoatant
的多次出现。
类型扩展
像表格这类数据的内容类型肯定都是由外部确立传递,因此要使用泛型。
竞态问题
图片位
网络正常的情况下直接请求展示数据即可。但当网络不稳定时可能会出现查询项和展示结果不一致的情况,也就是前一次的结果把后一次覆盖了。
解决方法
-
loading遮罩
直接将用户交互的部分挡住,或者将切换状态的button禁用也是一样的效果。为了保证用户体验可能需要做一些超时处理和新的UI。
-
取消请求
个人感觉的最优解?axios、fetch和Promise都可以取消。
-
不处理无用请求
也就是记录上一次点击的tag,不保存其他tag的数据,不过请求还是会正常发送,略微有些浪费资源,类似于两次握手和三次握手的关系?