软件自动更新原理

转载:https: www zhihu com question 24263552 answer 27216798在互联网不太普及的时代,很多软件公司还是会
转载:https://www.zhihu.com/question/24263552/answer/27216798

在互联网不太普及的时代,很多软件公司还是会发布离线更新包,这种更新包其实就是个安装程序,只是安装的内容是更新的部分,没有变化的文件直接用已经安装的版本的。此外这些安装程序还会修改一些系统配置以适应新版本的功能,比如注册COM组件,修改注册表等。

现在互联网已经十分普及,几乎所有软件都采用在线升级,具体实现上,有下列几种做法。

1. 比较简单的,通过http协议,检测是否有更新,就是把本地版本号发给服务器,服务器会返回一个配置文件,里面表明是否有新版本,并且带有新版本的下载地址,更新程序按照URL下载新版本的安装程序,然后执行这个安装程序,用户根据安装程序提示进行更新。

2. 再进化一步,每次都重新安装太麻烦,更新程序下载一个新版本的压缩包(zip/7z),然后帮用户解压缩到安装目录。现在客户端都追求简单设计尽量降低和系统的耦合,基本都是复制文件就算安装的那种绿色软件,所以把新版本的文件一更新就能用。

3. 如果某个软件体积已经比较大了,比如大于10MB,每次都下载一个完整的新版本的话,下载就太慢,安装也慢。既然本地已经有个安装版本了,每次更新其实变化的东西也不是很多,那么有个软件就下载一个更新文件的压缩包,然后解压缩到安装目录覆盖旧文件。

4. 有的软件更大,更新也频繁,每次更新的exe dll模块大多都有更新,所以还是要下载很大的更新包,于是有人用 bsdiff算法求新版本和旧版本的二进制差异,如果新版本就是修了bug改了几行代码,那么bsdiff生产的补丁也就几KB,下载的二进制补丁用bspatch来更新本地版本。bsdiff的算法库很小,大约才30+KB很容易集成到更新程序里。

5. 有的软件更更大,即使用bsdiff产生的补丁还是很大,有人搞出了更给力的补丁算法,Chrome的方法,
从介绍看,它比bsdiff生成的补丁还要小一个数量级,这个想法相当巧妙,对于那种代码模块为主的程序尤其有效。很多时候我们只改了几行代码,但是DLL模块却改变了很多,主要是因为代码优化链接时重排造成的,如果比较汇编代码差异就会很小,Courgette就把DLL反编译成汇编码,然后和旧版本的汇编码比较得到差异,更新的时候把旧版本也反编译打补丁然后再编译成DLL。这样的话如果只改了几行代码,那么生成的补丁可能就几十个字节。

6. 随着更新包越来越大,更新下载和安装的时间也越来越长,造成用户长时间等待,有的软件采用了后台下载后台更新的方式。所谓后台下载就是无论用户是否点了立刻更新,只要有新版本就在后台偷偷给用户下载下来,有点流氓,但这也是为了用户体验呢。那么后台更新呢,正在运行的程序,每个文件都是被占用的,是不能更新的。还是Chrome想出来的,双目录更新,把就版本先复制到另一个目录,然后更新这份新复制的,下次用户启动的时候就直接启动新版本。
比如 Chrome有如下的目录结构
Chrome
    +Application
        +35.0.1916.153
        +35.0.1916.114
         chrome.exe 

它用版本号做目录名,每次升级的时候更新新版本,旧版本在另一个目录运行不受影响。下次启动的时候 Chrome.exe永远加载最新版本的dll运行就好了。chrome.exe是个很小的程序,里面的逻辑就是检测下版本号加载最新版本的dll,这样简单的程序本身几乎不需要更新。


在需求的推动下,现代客户端的升级程序已经相当复杂了,包括了支持灰度放量的新版本检测,http断点续传下载,MD5完整性校验,bsdiff/courgette 二进制补丁更新,双目录迭代升级等技术。到了移动app时代,以上这些技术都用不到了,操作系统垄断了升级机制,只能通过操作系统检测下载安装更新,iOS做得相当彻底,Android还给app留了条自己下载apk安装的路,但是二进制补丁完整性校验等就彻底不需要app开发者自己操心了。