增量更新

著作权归作者所有。
商业转载请联系作者获得授权,非商业转载请注明出处。
作者:Lucas
链接:http://zhuanlan.zhihu.com/internet-thinking/19887025
来源:知乎

2.1 服务模型

一个基本的流程是:

  1. 客户端(软件本身或者是App Store) 将本地的软件的识别号和版本汇报给增量更新的服务器端。 识别号可以是包名等用来唯一识别一款软件的标示符。 在Android 应用中,可以使用APK的包名来表示,版本号可以使用apk的version_code.
  2. 服务器收到客户端的请求, 查询数据库,找到对应软件的最新版本号。
    • 如果等于客户端版本号,返回"没有更新"。
    • 如果大于客户端版本号, 返回给客户端最新版本号、下载文件的URL 以及新版本说明等信息。
      • 如果服务器端能够为客户端找到或者生成对应的patch 文件, 返回patch 文件的URL。
      • 如果因为服务器端因为各种原因不能生成patch 文件, 返回完整安装文件的URL。
    • 这里服务器端版本号是不会小于客户端的, 如果小于客户端版本号, 则是非法请求,应返回error。
  3. 客户端:
    • 客户端收到服务器返回结果, 如果没有更新, 结束本次查询。
    • 如果发现有更新,通过服务器端返回信息,提示用户或者静默下载文件。
      • 如果下载的是patch 文件, 则启动增量更新,和本地的老版本文件一起生成新版本文件。 启动安装过程。如果此过程失败, 可以尝试切换到请求下载完整安装文件。
      • 如果是完整的安装文件, 则可以直接启动安装过程。
  4. 更新完毕或者失败。

这里有两个关键步骤:

  • 服务器端通过diff生成patch 文件
  • 客户端通过patch 算法还原新版本软件

这两个步骤在具体实施时需要考虑一些现实因素。

2.2 服务器端

2.2.1 lazy 策略

如果有很多用户, 客户端可能就会用很多活跃版本。 我们假设软件有6个版本1.0~1.5,最新版本是1.5。这意味服务器端需要准备的patch 文件为C(6,2)= 15。 6个版本服务器端需要存储15个文件!仔细想一下,就能发现这些patch 文件并不是被均匀访问的。 比如diff(1.0~1.4), 在发布最新1.5 版本的时候, 可能大部分用户都已经升级到1.4版本了。 这其中有一些组合是没有必要存在的。
这里有一个办法, 不用提前生成所有的组合,而是使用lazy 策略, 即如果有用户访问,实时生成patch并缓存下来, 之后的用户再访问的时候可以直接访问缓存的文件。 这样避免了生成和存储很多不必要的patch文件。

2.2.2 diff 性能

如果使用了lazy 策略, 需要特别关注diff 算法的性能问题。 diff 是非常耗费资源的。bsdiff 算法需要内存 max(17n,9n+m)+O(1) bytes, 其中 n 为老文件的大小, m为新文件的大小。为了维护性能的稳定, 将后端的diff服务同更新服务本身分离出来对于维护服务的稳定性很重要。 bsdiff 也有可能会造成死机。如果当第一次请求diff时生成patch文件超过指定时间, 建议更新服务直接切换到全量下载,不用block 第一个用户的更新过程, 等到patch文件生成之后后续的用户可以使用增量更新。

2.2.3 磁盘清理

当发布了最新的1.5版本, 如果软件不用降级,即只允许用户升级到最新版本,服务器也可以考虑将1.0~1.4之间的所有patch文件清理删除掉。这样能够节省部分硬盘,防止文件指数膨胀。

2.3 客户端

2.3.1 性能

设计patch算法的时候需要考虑客户端的硬件性能和运行环境。 对于服务器端来说, 是软件开发者可控制可投入资源升级硬件性能的。而对客户端来说, 软件开发者一点办法也没有。 用户的硬件设备可能多种多样, 需要考虑最坏的情况。

比如Android平台, C++版本的bspatch 算法就比java 版本的要快10倍以上。如果在Android上使用Java 来做bspatch,所带来的性能问题将使得增量更新不具有更多的优势,因为patch花费太久时间了。

这就涉及到一个全局优化的问题。当我们选择一种优化方案时, 我们希望整体上做全局优化。假设增量更新是使得下载的文件减小了10%,但是在客户端的patch过程却多花了10s,这是得不偿失的。 另外diff 算法得到的patch 文件总不是会变小。现实环境是复杂的。 如果一个用户从一个很老的版本更新到最新版本的时候,老版本和新版本的差异会很大, 可能delta文件比新文件还要大,这个时候还继续采用增量更新服务显然不是一个明智的策略。

综上所述, 服务器端应该通过patch 文件和新文件大小比较做一个综合决定是返回全量更新的结果还是增量更新的结果。

获取老文件

一些平台上获取老版本的文件可能会是一个问题。 在Windows 平台上, 安装文件(.iso或者exe安装文件)安装完之后就被删除了, 并不会在系统中保存。这就要求软件本身去追踪安装后的每个文件,对每个文件实施增量更新。或者保存一份安装文件。 Chrome 就是这么干的。

在Android 平台上, 系统保留apk 文件。 如何获取呢?参考:

The apk is located at /data/app/com.package.name.apk.
Get APK of installed app
android - Question on .asec (encryption) on Froyo 2.2 / installing app onto sdcard

2.4 实验

为了说明性能上优化,我们在此拿"豆瓣电台"这个应用的APK文件做一个实验, 大体能说明采用增量更新可以节省70%左右的文件传输。

环境

  • 硬件

        Model Name: MacBook Pro
        Model Identifier:    MacBookPro8,1
        Processor Name:    Intel Core i7
        Processor Speed:    2.8 GHz
        Number of Processors:    1
        Total Number of Cores:    2
        L2 Cache (per Core):    256 KB
        L3 Cache:    4 MB
        Memory:    4 GB
        Boot ROM Version:    MBP81.0047.B27
        SMC Version (system):    1.68f98
        Serial Number (system):    Not Available
        Hardware UUID:    **************************************
        Sudden Motion Sensor:
        State:    Enabled
    
  • 软件
    bsdiff / bspath
    安装: 在Mac OSX 上使用 brew:

      brew install bsdiff
    

    Linux 有相应的工具 bsdiff and bspatch. 见 Binary diff

  • 文件列表:

      -rw-r--r--  1 lucas  staff  282499 Sep 13 14:57 com.douban.radio.2.1.3-4.patch  
      -rw-r--r--@ 1 lucas  staff  960038 Sep 11 17:30 com.douban.radio.2.1.3.apk
      -rw-r--r--@ 1 lucas  staff  891700 Sep 11 17:46 com.douban.radio.2.1.4.apk
      -rw-r--r--  1 lucas  staff  891700 Sep 13 14:58 com.douban.radio.2.1.4.apk.patched
    
  • com.douban.radio.2.1.3.apk 和 com.douban.radio.2.1.4.apk: 相邻两个版本的Douban FM 应用.

  • com.douban.radio.2.1.3-4.patch: The diff of com.douban.radio.2.1.3.apk and com.douban.radio.2.1.4.apk. 通过命令生成:

    bsdiff com.douban.radio.2.1.3.apk com.douban.radio.2.1.4.apk com.douban.radio.2.1.3-4.patch
    
  • com.douban.radio.2.1.4.apk.patched: com.douban.radio.2.1.3.apk applied with the patch.通过命令生成:

    bspatch com.douban.radio.2.1.3.apk com.douban.radio.2.1.4.apk.patched com.douban.radio.2.1.3-4.patch
    
  • 验证:

    lucas-mac:experiment lucas$ md5 com.douban.radio.2.1.4.apk
    MD5 (com.douban.radio.2.1.4.apk) = 6f730c01a40dd3b7b9f034fe649ac4e8
    lucas-mac:experiment lucas$ md5 com.douban.radio.2.1.4.apk.patched 
    MD5 (com.douban.radio.2.1.4.apk.patched) = 6f730c01a40dd3b7b9f034fe649ac4e8
    

  • 解释
  • 新文件 com.douban.radio.2.1.4.apk 大小 891700 bytes, patch 文件大小仅仅是282499 bytes. 比例 31.680946506%, 节省了 68.319053493% 带宽。
  • bsdiff and bspatch 在Mac Pro CPU Intel Core i7 2.8G, Memory 4GB 电脑上运行时间小于1s.

你可能感兴趣的:(android)