PC客户端软件升级方式简史

  • 在windows8之前,微软的Windows平台一直没有提供一个想苹果的AppStore或者Linux的包管理这样的统一软件管理工具。所以Windows下的软件安装、升级、卸载的事情一般都是软件自己去负责。这样导致Windows下的软件安装、升级、卸载的方式五花八门,但总体上来说方法都大同小异。安装程序主要分两种,下载器的安装包和离线安装包,这个不赘述。
  • 今天重点聊一下升级,升级功能看似简单,但对于一个想持续经营的客户端软件来说却是一个重要的生命线。开发团队辛苦修改的bug、做的新功能都希望用户能马上通过升级新版本体验到。
  • 在互联网还没普及的蛮荒年代,很多软件公司升级都是发布离线升级包,一般这种包就是一个安装程序,它只负责安装程序需要更新的部分,然后做一些修改注册表之类的系统配置以适应新版本的功能。
  • 现在互联网普及后,所有的PC客户端软件基本上都是使用的在线升级。

    • 最简单的在线升级方式是首先客户端发送检测更新的消息到服务器,服务器给返回是否有新版本,最新版本号以及下载地址等信息,客户端就根据这些信息处理。如果有更新就去刚刚获取到的地址下载最新的安装程序,然后执行安装程序更新。
    • 后来大家觉得每次都重新安装太麻烦,而且安装包也特别大,下载也非常耗时。于是这个下载的程序被替换成了一个压缩包,里面装的是程序需要更新的文件。升级程序下载好压缩包后再解压到安装目录中就完成了软件的升级。
  • 随着敏捷开发方式的普及,软件的升级就变得越来越频繁了。对于PC客户端软件每次升级时主程序以及一些重要的动态库都有可能更新,所以下载的压缩包也会比较大。于是就产生了比较文件二进制差异的算法BsDiff,以及Google基于其进一步改进的Courgette(小胡瓜)。这些算法的加入可以让补丁包缩小了n个数量级。这样需要客户端去下载的压缩包就会很小了,下载耗时也会大大缩短。
  • 另外值得一提的是,随着软件升级包大小越来越大,用户下载更新文件的等待时间也越来越长,于是有些软件就采用了后台静默下载的方式。这种方式虽然流氓,但可能对于用户来说体验要好一些。那么这种情况下主程序一般都还在运行,而升级程序下载完成后想要更新文件立即升级就必须关闭主程序然后进行文件替换,以免文件被占用,导致升级失败。于是Google的chorme搞出一个双目录更新的方法来应对这种情况。所谓双目录更新就是把原来的文件先复制到另一个目录下,更新程序的时候就更新这个文件目录,升级完成后就直接从新的目录中启动新版本。
  • chrome的目录结构是这样的:
Chorme
    +Application
        +57.0.2987.110
        +57.0.2987.88
            chrome.exe
  • 可以看到,他是以版本号做目录名。以后启动chrome.exe时去加载最新版本就可以了。当然它能这样做主要因为chrome.exe本身是个很小的程序,基本它自身是不需要升级的,它主要负责的就是检测版本号然后加载新版本的dll。
  • 当然现在的客户端升级程序还涉及一些入灰度,md5完整性检测断点续传等技术这里也不在赘述。下面我简单介绍一下BsDiff和Courgette。
BsDiff: Linux中的一个开源工具,致力于快速和轻量的更新Linux的操作系统漏洞(跟微软的安全补丁类似),其算法的核心思想是基于统计学规律进行近似匹配,然后通过一系列的变化(比如BWT变换)提高“近似段”的压缩率。
Courgette: Google Chrome升级系统的核心模块,基于BsDiff,但对其进行了一系列的改进,将平台相关的信息(即x86汇编指令)融入其中,以期望更精确的定位指针,从而避免统计算法在差异明显时候的错误率。
  • Google官方给了一个10M的升级包例子使用bsdiff可以看到包小了不少,用Courgette更是少了几个数量级。

  • 使用bsdiff算法我们的升级过程是这样的:
server:
    diff = bsdiff(original, update)
    transmit diff

client:
    receive diff
    update = bspatch(original, diff)
  • 大致流程就是这使用bsdiff算法比较不同版本的二进制文件制作补丁包,客户端下载补丁包后调用bspatch生成新的二进制文件。
  • 使用Courgette的升级过程是这样的:
server:
    asm_old = disassemble(original)
    asm_new = disassemble(update)
    asm_new_adjusted = adjust(asm_new, asm_old)
    asm_diff = bsdiff(asm_old, asm_new_adjusted)
    transmit asm_diff

client:
    receive asm_diff
    asm_old = disassemble(original)
    asm_new_adjusted = bspatch(asm_old, asm_diff)
    update = assemble(asm_new_adjusted)
  • Courgette对于bsdiff的优化主要就是在adjust这一步上,具体可以参考Courgette官方说明
  • 最后,Google还开源了一套Windows下的升级协议,大家有兴趣也可以研究下omaha

你可能感兴趣的