打包下载服务

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

摘要如下

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

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

实现

要求

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

即刻下载

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

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

浏览器支持

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

独立可复用

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

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

并发访问

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

资源占用小

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

安全

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

高可用、高并发

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

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

设计

传统的文件打压缩包方案

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

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

性能考虑:放弃压缩

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

  • 用户最需要的是打包,即免去一个一个下载文件的繁琐。
  • 用户关心的是能否立刻开始下载,而不苛求下载的文件大小与时间。
  • 用户打包的文件中,除了文档之外,如照片、视频、PDF文件可压缩尺寸的余地太小,压缩反倒徒劳。

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

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

资源考虑:按流处理

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

我们只要明白并处理好

  • 无压缩的打包只是把源文件内容一个一个接起来,形成一个文件而已。
  • 文件下载起始需要一个预估大小,只能多,不能少。

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

并发考虑:Golang实现web服务

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

  • 多进程较为消耗资源,考虑使用多线程或协程
  • I/O 为主,考虑使用贴近底层的语言
  • web 服务选简单些的框架,不需要 view、不需要 session、不需要关系数据模型

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

安全考虑:secure-token auth

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

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

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

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

  • 不耦合任何产品里的业务和代码
  • 不存储任何产品的数据
  • 容器化,把代码打成docker image,按需要的配置启动container
  • 只接受可直接访问的源文件地址,并在打包前发option请求尝试访问
  • auth算法是公开的,只有key pairs是服务自己存储的,前一节提到过

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

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

小心文件名
  • IE和现代浏览器对UTF8文件名的对待是有区分的,我们在服务端都满足它们。
  • 不同浏览器对特殊字符的转换也是有差异的,我们在服务里直接统一改好名,同时做到区别重名。
别拼接源文件地址进 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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,186评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,858评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,620评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,888评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,009评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,149评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,204评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,956评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,385评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,698评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,863评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,544评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,185评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,899评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,141评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,684评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,750评论 2 351

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,894评论 25 707
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,642评论 18 139
  • Ubuntu的发音 Ubuntu,源于非洲祖鲁人和科萨人的语言,发作 oo-boon-too 的音。了解发音是有意...
    萤火虫de梦阅读 99,217评论 9 467
  • Spring Boot 参考指南 介绍 转载自:https://www.gitbook.com/book/qbgb...
    毛宇鹏阅读 46,788评论 6 342
  • 今天提起健身的意念了,就因为现在我已经感觉我是一个虎背熊腰的女人了,实在已经不能说美了,好郁闷,所以一定要健身了。...
    幽兰依依阅读 133评论 2 1