Chrome的差量升级Courgette

Windows客户端做程序的升级是一件比较复杂的事情。不像移动端,有专门的移动商店用来安装和更新程序,Windows客户端程序升级需要自己制作安装包,放到服务器上供用户下载,后续还需要提供升级服务。由于用户操作不可预知性和用户环境的复杂,安装跟升级都会发生各种异常而失败。迄今为止Windows上也没有一个好的程序安装、升级、卸载框架,这不得不说是Windows开发的悲哀。
而且目前程序的安装包越来越大了,动辄几十兆,用户下载安装包需要很长的时间,这也是导致安装升级成功率低的一个原因。所以,安装升级包越小越好。对于这个问题,有两个解决思路:

  1. 程序功能模块化,把代码编译到不同的dll里面。如果某个模块有修改,就只需要升级相应的dll就行了。
  2. 找出旧版本和要升级的新版本文件的差异,把差异文件发给客户端。客户端根据旧版本和差异文件还原出新版本。

对于第一种方案,优点是实现起来比较简单,我之前的老东家360很多产品就是用的这种方案。这个方案的缺点也很多:

  • 把功能模块化,这个升级粒度依然有点大,有些dll可能只是修改了几行代码就需要升级这个几兆的dll文件。
  • 如果底层模块的接口改了,那么调用它的上层模块都需要修改,这就导致升级会涉及到很多文件。
  • 如果模块代码划分不清或者有一些隐式的逻辑依赖,这种dll升级可能引入难以觉察的bug,甚至会导致程序崩溃。

第二种方案已经有了bsdiff了。但是对于Widnows客户端程序,依然存在很大的挑战。因为C++这种需要编译的代码,充满了内部地址引用,一些指令直接包含其他指令的地址或者位移,几行代码的修改就会导致许多地址值的改变,结果导致最终生成的二进制文件差异很大。

Google的Chrome开发团队特地为C++这种需要代码编译的程序开发了一种叫做Courgette的差异算法。原理是先把可执行的二进制程序进行反汇编,找到那些内部的地址指针,把它们替换成一个符号,然后再用常规的办法生成差异文件。

下面是常规的bsdiff升级:

这个是Courgette升级:

不得不佩服,这是个绝妙的思路。没有对C++、编译器、汇编、PE文件格式深厚了解的人,是无法设计出如此精妙的方案。
Chrome团队还有一个数据的对比:

Full update              10,385,920
bsdiff update           704,512
Courgette update    78,848

对PE文件来说,采用Courgette做差异的算法比bsdiff小了一个数量级。

Chrome的升级是采用了Courgette,对应的开源Chromium默认是没有升级功能的。不过我们编译mini_installer工程的时候,在gn/gyp里面加上下面几个命令行就可以了:

我们把旧版本的chrome.7z和setup.exe文件放到c:/temp/base目录里面,就会生产了差量的mini_installer。

当然我们也可以手动生成差量文件,编译好courgette.exe:

gen是生成差量文件,apply是应用差量文件。

参考:

  • https://dev.chromium.org/developers/design-documents/software-updates-courgette
  • http://neugierig.org/software/chromium/notes/2009/05/courgette.html

发表评论

电子邮件地址不会被公开。 必填项已用*标注