网盘秒传
存储介质使用Mongo的GridFS技术实现,同时采用Mongo的Replica Set与Sharding。
应用采用分布式的环境进行部署,现架构采用Memcache、ActiveMQ、MongoDB、MySQL。
Application Scenarios
多人上传相同文件或者单人上传多份相同分件,进度条是真实上传进度,当其中一个用户上传完成,其余人也同时变为秒传,同时服务器上只存储一份文件数据,同时支持高并发。
Thinking:
Each file has a MD5 value,the same file names,the same MD5 value。
Solution
Use memcache
当上传文件时,首先验证该MD5值是否存在于Memacache。如果不存在则在Memcache服务器中根据MD5值设置一个标识,比如Uploading。当后续的请求检测到是Uploading,也可正常上传,相互不影响。设置一个检查时间,上传线程每隔多少秒则到Memcached中检测状态是否发生改变,如未发生改变,则继续读取数据流内容,当其中一个线程上传完成时,则将状态变为Finished,此操作一定要保证原子性。其他线程检测到时,中断上传同时产生一个快照,指向底层的真实文件数据块,并反馈用户秒传。同时在系统任务中新增一条删除任务记录,通过JMS消息来实现,在服务器空闲时间段自动删除已经上传的块内容,避免数据冗余,避免出现数据冗余产生的读取问题和存储空间占用。
此种方式可以实现,但是对Memcache来说,压力稍大,也可能在检查时间点未到时,两人同时上传,此时冗余块也需要做处理,否则会产生数据错乱,这是一个问题,上传完成后还需要检查服务器是否同时存在两份数据,如存在则取其中一份即可。
Single Server
由于是分布式部署,原本想法是通过在多个线程在上传时,相同MD5值上传文件的线程,同时持有一个对象实例,对象实例中通过标识属性来通知其他线程是否上传完成,是否继续读流。比如通过回调设置标识状态:
while (!flag) {
//do something...
}
但是因为是分布式部署,此种方式行不通。
此方案虽可实现,但Memcache轮询压力稍大,待考虑。
Version Control
网盘上传文件时,Send check request,判断服务器中是否有相同MD5的文件存在,如果不存在,则调用newFile接口上传文件,否则调用updateFile接口,新增该文件版本。
调用UpdateFile接口生成新的FileVersion时,前端的按钮还处于可点状态,例如暂停和开始,因为还没有接收到请求响应,响应如果有延迟的话,此时多次点击暂停开始的话,会发送多个check和updateFile请求,会产生多个文件版本。
网盘已经存在文件A,有两种情况会产生多个版本:
1.客户端再次在相同位置上传同名文件。
2.多个客户端上传相同文件。
目标
文件版本正常叠加,并且当同一客户上传相同文件通过多次点击暂停和开始还是上传多条相同文件时版本号只发生一次叠加。不同用户上传相同文件时,多少个用户则产生多少次叠加操作。
Question:
单用户上传相同文件,通过多次点击暂停和开始,产生多个版本号,是因为在检查秒传的过程中,请求可能还未响应,此时多次点击,会发送多次秒传请求道后台,所以产生多个版本号。
Solution:
类似于表单的重复提交,网络带宽和服务器响应中间可能存在延迟,频繁的点击上传开始和暂停按钮与平常的表达提交多次点击是相同的效果。此时可以在调用UpdateFile接口时使按钮变灰,当响应后再使按钮可点击。
欢迎各位指出缺点和提出好的建议。