打包下载服务

2017-10-16  本文已影响0人  拙嘴笨腮

有组织就有协作,有协作就有共享,想把共享作为资源保留就要屯盘建库。你产品的用户接下来会问:“我能否把选好的资源打包下载呢?”,衣食父母的吩咐这得接着。如何设计一个靠谱的打包下载服务呢?

摘要如下

要求
- 即刻下载
- 浏览器支持
- 独立可复用
- 并发访问
- 资源占用小
- 安全
- 高可用、高并发

设计
- 性能考虑:放弃压缩
- 资源考虑:按流处理
- 并发考虑:Golang实现web服务
- 安全考虑:secure-token auth
- 独立可复用考虑:独立无状态服务
- 浏览器支持考虑:小心处理
- 高可用、高并发考虑:集群方案

实现

要求

注定是产品经理和技术总监都必须满意的,架构师心里暗自打鼓。

即刻下载

产品经理说了:点击打包下载按钮之后还提示让用户等 “ZIP文件准备就绪,请点击链接” 的通知再开始下载,这是不能接受的,说好的无需等待呢?

技术总监说了:点击打包下载按钮之后就 Loading 十几秒甚至几十秒直到下载开始,这是不能接受的。性能要好,否则大文件的话到底等到什么时候去?

浏览器支持

产品经理说了:产品的用户群挺广,用什么浏览器的用户都有,都能支持的吧?特别是从IE下载,文件名可不能是乱码的。

独立可复用

技术总监说了:做一个服务,就要能独立部署,要能被几个产品复用。每个产品都来一套的话,可接受不了。

产品经理说了:每个产品要下载的资源可能来自不同地方,要能支持任意源地址

并发访问

产品经理说了:我们估计了初期同时使用打包下载的用户数,支持千人并发访问就可以了,图片文档下载嘛文件都不大,我们会限制让用户一次不能下载很多文件的。

资源占用小

技术总监说了:经费有限,初期用户用得也不频繁,这个服务只能给你们划一台低配置的VM,2核CPU、2G内存、20GB硬盘够了吧,日志不能丢哦,好好干......

安全

产品经理说了:网上很多资源都存在被盗链或窃取的情况,用户A打包下载zip文件之后,这个zip文件不会被其他人再下载或盗链吧?要保证安全哦。

高可用、高并发

技术总监说了:用户会积少成多,架构设计要走在前面。高可用性如何保证?更高的并发访问如何应对?

不断点头称是的架构师此刻脑子飞快的运转着,一面在心里再次确认这些要求不是空想,一面如玩魔方一般拼凑思路进行设计。

设计

传统的文件打压缩包方案

步骤如下,缺点在括号内。这些资源消耗在并发量增大时会急剧上升,VM资源成为瓶颈。

  1. 从各源地址下载源文件到服务器(有等待时间,源文件临时占硬盘)
  2. 打包压缩(有等待时间,压缩很耗CPU)
  3. 提供压缩包文件可对外下载(压缩包占硬盘)

性能考虑:放弃压缩

在前文列出的《要求》里,并没有着重提到打包的压缩比,其原因

放弃压缩之后,拿到源文件的第一块内容之后就立刻可以生成打包文件了,省下CPU资源,省下等待时间。假设要求输出是ZIP包,无压缩的包仍是ZIP格式。

对纠结用户下载流量大小的人,告诉你打包下载的内容是经过 gzip 再由浏览器解开的,你可以安心了。

资源考虑:按流处理

既然放弃压缩,省下的工作都是I/O,即搬运源文件内容到服务器,再以一个文件的形式搬运到用户手里,为什么中间要存一次文件呢?我们大可以把前后两个管道接上,拿到的每一块源文件内容都立即倒手给用户。

我们只要明白并处理好

按流处理之后,无论源文件或打包产出文件都无需存在硬盘上了,剩下硬盘资源,剩下I/O时间。

并发考虑:Golang实现web服务

打包下载服务仍是请求响应模型的web服务。如何提高并发下载数?在资源紧张的情况下

结果 Golang 语言胜出,采用 Golang 内置的 http 库由 goroutine 支持并发。

安全考虑:secure-token auth

打包下载服务授予 key pair 给服务使用者,并约定共同的hash算法,由服务使用者发出请求前做签名生成 token,之后由打包下载服务验证签名,并进一步验证是否过期以反盗链。

在打包下载服务这里实际只做了验证(authentication),而没有做授权(authorization)。没错,任何签名正确且未过期的请求都可以被放行。你应该注意到了,签名请求的是服务使用者,那是你的某个使用此服务的产品,不是最终用户,对最终用户做授权是各产品自己的事,这也是为独立性的考虑。

独立可复用考虑:独立无状态服务

服务中必须把独立做到极致,才易复用

浏览器支持考虑:小心处理

这是些十分大众又出名的坑,小心处理就是。

小心文件名
别拼接源文件地址进 URL

提出打包下载请求肯定要带着若干源文件的地址,发一个 POST ajax 请求比较理想。因为若选 GET 请求只能拼 URL,源文件地址的URL长度和数目未知,存在超过 URL 长度限制的可能(IE有2084个字节的限制)。
POST 请求带回一个固定长度的存根,在回调方法里再组装一个下载打包文件的 URL,直接打开它就可以启动下载了。这两个请求的衔接是一个用户无法干预的连贯动作,所以时间很快,所以把存根存放在内存数据库如 Redis 中可以设一个很快的过期时间。

高可用、高并发:集群方案

看了前文,你该知道这个服务被设计成独立无状态的。和其他集群一样,一个负载均衡器就可实现。

但是前文我说过是用 docker 部署的,我推荐在几个VM之间建 docker swarm,直接利用 docker 的 ingress 网络特性来做负载均衡,通过横向扩展 docker swarm 中节点数目和container数目来支持更高并发。

存根依赖的内存数据库如 Redis 自然也需要做集群,负载很小,只为高可用。

实现

你很走运,我已实现好了,拿去吧~

demo: http://zipper.demo.wushaobo.info
github: https://github.com/wushaobo/zipper
docker: https://hub.docker.com/r/wushaobo/zipper

上一篇下一篇

猜你喜欢

热点阅读