IM开发干货分享:IM客户端不同版本兼容运行的技术思路和实践总结

Posted im中国人

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了IM开发干货分享:IM客户端不同版本兼容运行的技术思路和实践总结相关的知识,希望对你有一定的参考价值。

本文将基于笔者的IM产品开发和运营实践,为你分享如何实现不同APP客户端版本与服务端通信的兼容性处理方案。

本文由巩鹏军分享,原题“IM兼容性基建”,本文有修订。

1、引言

一个成熟的IM成品,在运营过程中随着时间的推移,会发布不同的版本,但为了用户体验并不能强制要求用户必须升级到最新版本,而服务端此时已经是最新版本了,所以为了让这些不同客户端版本的用户都能正常使用(尤其IM这种产品,不同版本可能通信协议都会有变动,这就更要命了),则必须要针对不同客户端版本的兼容处理。

本文将基于笔者的IM产品开发和运营实践,为你分享如何实现不同APP客户端版本与服务端通信的兼容性处理方案。

 

学习交流:

(本文已同步发布于:http://www.52im.net/thread-4202-1-1.html

2、关于作者

巩鹏军:专注移动开发十多年,热爱即时通讯技术。个人微信公众号:“巩鹏军”。

作者在即时通讯网分享的另一篇《知识科普:IM聊天应用是如何将消息发送给对方的?(非技术篇)》,感兴趣的读者也可以看看。

3、一个App时怎么办?

提示:“一个App”指的是同一个IM服务端,只服务于一个特定的IM产品。

首先想到的就是直接使用App版本号判断新老版本并进行兼容处理。

如下图所示:

一般来说,不同的IM客户端(如iOS、Android、Windows、Mac)都是同步迭代,多端发版时间一致,App版本号也一样。

所以用跨多端的App版本号可以很容易地让服务端只用写一遍判断和兼容逻辑。

示例:假设从V2.1.0开始应用红包消息,那么判断客户端是否支持红包的逻辑就很简单。

伪代码如下:

booleanisSupportRedEnvelop(String appVersion)

 returngte(appVersion, "2.1.0");

附:版本号比对逻辑(未充分考虑异常情况):

List<Integer> toNums(String version)

  Matcher matcher = Pattern

 

    .compile("/[0-9]+\\\\.[0-9]+\\\\.[0-9]+")

 

    .matcher(version);

 

  String versionString = matcher.find()

 

    ? matcher.group(0).substring(1)

 

    : "1.0.0";

 

  List<Integer> verNums = Arrays

 

    .stream(versionString.split("\\\\."))

 

    .map(Integer::valueOf)

     .collect(Collectors.toList());

  returnverNums;

 

booleangte(String version, String target)

 

  List<Integer> appVerNums = toNums(version);

  Integer appMajor = appVerNums.get(0);

  Integer appMinor = appVerNums.get(1);

  Integer appPatch = appVerNums.get(2);

  List<Integer> targetNums = toNums(target);

  Integer targetMajor = targetNums.get(0);

  Integer targetMinor = targetNums.get(1);

  Integer targetPatch = targetNums.get(2);

  return(appMajor >= targetMajor) ||

         (appMinor >= targetMinor) ||

         (appPatch >= targetPatch);

4、多个App时怎么办?

4.1概述

提示:“多个App”指的是同一个IM服务端,可能作为通用服务,作为多个不同APP产品中的聊天模块使用的场景。

只有一个App时肯定是比较简单的。但现实情况是一套IM系统通常会用于多个业务场景,这是很普遍的现象。业界的知名IM产品,比如钉钉、飞书、企业微信、美团大象等都是这样。

底层逻辑大概是:IM系统比较复杂,功能繁多而且难以实现、更难以稳定,所以一个IM团队维护一套IM系统,然后应用在多个业务场景就是最具性价比的选择了。

4.2使用App版本

每个业务场景都会有自己的客户端App,每个App都有自己的版本号,那么根据App版本号判断新老版本的逻辑就不适用了(如下图所示)。

一个App时可以这样做兼容性判断:

booleanisSupportRedEnvelop(String appVersion)

 returngte(appVersion, "2.1.0");

多个App时的兼容性判断:

booleanisSupportRedEnvelop(String version)

    return

    (app.equals("App1")&>e(version,"2.1.0"))||

    (app.equals("App2")&>e(version,"2.2.3"))||

    (app.equals("App3")&>e(version,"6.1"));

4.3使用App版本号的麻烦

随着App的增多,需要的判断也越多,这会很麻烦,也很容易出错。

每个App推出新版本后,用户不可能瞬间就升级到最新版本,根据经验,每个App往往都会同时存在十个以上的不同版本。

这就会形成如下图所示的局面:

5、多个App时,可将IM能力提炼为一套公用代码

多个App时的问题总结起来就是:一套服务端代码如何适应集成了不同IM能力的不同App客户端?

我们来具体举例分析一下,假设一个IM团队维护的IM相关的客户端模块有IM Client SDK、联系人、长连接、朋友圈等四个模块(如下图所示)。

如上图所示:

  • 1)App 1:集成了全部四个模块;
  • 2)App 2:只集成了三个模块;
  • 3)App 3:只集成了三个模块。

因为三个App面向的客户群不同,发版节奏不同,所以各自集成的IM的能力也不同。

比如下面这样:

  • 1)App 1:面向内部员工办公沟通使用的App 1需要功能丰富,对于稳定性和Bug有一定的包容性,也容易沟通和修复再发版;
  • 2)App 2:面向客服场景,用于企业的客服专员和企业的C端用户沟通解决客诉问题,对于稳定性要求高,C端用户升级率不好控制,发版节奏慢,最快只能和主业务App一致;
  • 3)App 3:面向企业和B端供应商,比如美团和美团上的商户,京东和京东平台上的第三方商家,对于稳定性要求也比较高,B端商家的升级率好控制一点,发版节奏也可以快一些。

从上图可以看出,因为IM核心能力是同一个团队维护,所以Core包含的多个模块的代码必然是只有一套源代码。不同App只是Core集成打包出来的产物,或者说不同App只是Core外面套了不同的壳而已,只要Core一样,则App的IM能力就一样(这就是本节标题所述的“多个App时,可将IM能力提炼为一套公用的代码”这个意思)。

6、给每个App中使用的公用代码(Core)一个版本号

如上节所述,我们将IM能力提炼为一套公用代码(以下内容简称“Core”)。

那么,我们能不能给Core一个版本标识呢?

答案是肯定的:

站在App的角度,每个App相当于打上了Core版本标签:

7、如何正确地解读Core版呢?

7.1抛开App看Core版本

如果不看App版本,只看Core版本标签:

7.2从一套服务端代码看Core版本

同一个IM团队,其IM Servers必然也是同一套代码集,不考虑部署的区别。

那么上图逻辑上等价于下图:

7.3使用Core版本的兼容性判断

站在Core的视角,多个App就像单个App类似,只是使用的版本标识不同。

具体如下:

  • 1)单个App时,IM服务端要区分不同App版本;
  • 2)多个App时,IM服务端要区分不同Core版本。

还拿是否支持红包的判断举例。

一个App时:

booleanisSupportRedEnvelop(String appVersion)

 

returngte(appVersion, "2.1.0");

多个App时:

booleanisSupportRedEnvelop(Integer coreVersion)

 

 returncoreVersion >= 2;

通过Core版本号,我们可以把兼容逻辑判断简化到和单个App一样的简单。

8、关于Core版本的命名和取值

关于Core版本号的取值,有下列可能的选项:

  • 选项一:语义版本号 1.2.0;
  • 选项二:整数 自然数 1 2 3;
  • 选项三:整数 迭代日期 20220819 或 220819。

因为Core版本号不用给最终用户看的,无需遵循常见的语义版本号规范。而且Core版本号只用于版本对比,所以整数会是一个比较好的选择,方便比较,准确可靠。

用自然数 1、 2、 3作为Core版本号是可以的,每个迭代发布新的Core版本时递增一下就可以了。

但是考虑到有多个终端平台iOS、Android、Windows、Mac,如果某个平台的Core发布后发现小Bug需要HotFix,那么要递增版本号,就会挤占其它端的下一个自然数。究其原因,在于自然数是连续的,没办法在两个常规的版本间插入一个HotFix版本。

选项三就可以解决这个问题:因为Core的迭代发布日期是稀疏的,若干天后才会发布一个Core版本,那么当某个端需要一个HotFix版本时,选择HotFix当天的日期作为版本号即可。

总体上:多个端的主要版本号都是约定的统一的发布日期,多端一致,同时允许某个端临时HotFix插入一个新的版本号,保留弹性。

参考 Google 对Android SDK API版本的实践,我们可以把Core版本号命名为core_level,取值为Core的发布日期的整数表示。

9、多个App情况下的其它版本标识

1)platform:

一套Core,不同端在实际开发中,可能存在差异,为了针对具体端进行特定的兼容,需要知道当前是哪个端,可以约定platform字段表示端。取值可以是:ios、android、win、mac、linux等。

2)App版本号:

在IM相关逻辑的兼容性判断中,只需使用跨App的多端一致的core_level了。但是为了和最终用户、产品经理等沟通方便,保留App版本号app_version用于人和人之间沟通交流。core_level主要用于研发工程师之间,还有工程师和程序之间的沟通。两者各取所长。

10、版本标识的传输方式

每个API和每条长连接数据包都携带Core版本,这样服务端可以无状态得处理每一个请求。如果需要在服务端主动推送时区分目标端的版本,可以在App登录时将其携带的Core版本落库存储,然后推送时查询使用。

10.1短连接(HTTP)

HTTP短连接通过新增Header字段方式传输:

curl "https://domain/api/v1/xxx"\\

  -H "platform: ios"\\

  -H "app_version: 8.0.25"\\

  -H "core_level: 220819"

10.2长连接(Socket)

长连接SDK通过类似HTTP Header的方式传输:

  "platform":"ios",

  "app_version":"8.0.25",

  "core_level":"220819"

 

10.3短转长

短转长时HTTP Header会转换为长连接数据body里的header通过长链传递。

这样就同时存在长连接header和长连接body.header两套字段,最终以长连接body.header为准即可。

10.4其它

IM系统里的浏览器和小程序,如果可以新增HTTP Header则新增Header传输,实在没有办法可以通过User-Agent传输该信息,服务端优先解析Header,没有找到时再解析User-Agent。

服务端解析UA的正则表达式:

/ platform\\/(ios|android|mac|win|linux) app_version\\/([0-9]\\.[0-9]+\\.[0-9]+) core_level\\/([1-9][0-9]+)( |$)/

以上正则表达式在线运行效果:点此查看

11、本文小结

至此,我们找到了一个适用于多个App、多个子模块、多个功能点、临时BugFix的版本标识:Core版本号,这样就可以很好地解决多App的IM能力兼容性问题。

以下是版本兼容性判断伪码:

booleanisSupportRedEnvelop(Integer coreLevel)

  returncoreLevel >= 220819;

12、参考资料

[1] Browser vs Engine Version

[2] Node.js ABI version number

[3] Android SDK API Level

[4] 零基础IM开发入门(一):什么是IM系统?

[5] 一套海量在线用户的移动端IM架构设计实践分享(含详细图文)

[6] 一套原创分布式即时通讯(IM)系统理论架构方案

[7] 从零到卓越:京东客服即时通讯系统的技术架构演进历程

[8] 一套亿级用户的IM架构技术干货(上篇):整体架构、服务拆分等

[9] 基于实践:一套百万消息量小规模IM系统技术要点总结

[10] 一套十万级TPS的IM综合消息系统的架构实践与思考

[11] 从新手到专家:如何设计一套亿级消息量的分布式IM系统

[12] 闲鱼亿级IM消息系统的架构演进之路

[13] 深度解密钉钉即时消息服务DTIM的技术设计

[14] 一套高可用、易伸缩、高并发的IM群聊、单聊架构方案设计实践

[15] 企业微信的IM架构设计揭秘:消息模型、万人群、已读回执、消息撤回等

(本文已同步发布于:http://www.52im.net/thread-4202-1-1.html

IM跨平台技术学习:得物基于Electron开发客服IM桌面端的技术实践

本文要分享的是得物技术团队基于Electron开发客服IM桌面端的技术实践过程,内容包括桌面技术选型、Electron的基础概念、具体的实施技术方案、遇到的棘手问题等。

本文由得物技术团队Uni分享,即时通讯网收录时有内容修订和排版优化。

一、引言

本文要分享的是得物技术团队基于Electron开发客服IM桌面端的技术实践过程,内容包括桌面技术选型、Electron的基础概念、具体的实施技术方案、遇到的棘手问题等。

Electron社区虽然很活跃,但是不一样的场景遇到的技术问题,几乎找不到对应的解决方案,我们很多都是在探索过程中不断的去完善,希望本文能带给你一些启发。

(本文已同步发布于:http://www.52im.net/thread-4159-1-1.html

二、系列文章

本文是系列文章中的第7篇,本系列总目录如下:

三、业务背景

随着公司业务的快速发展,商家客服也纳入了我们的服务范围,商家客服工作台的定位是通过工具和数据服务商家,一站式解决用户购买咨询诉求。通过工具和运营策略协助商家提升服务品质,让品牌商家有动力运营好潜在的客户,从而达到提升用户服务的目标。

已有web端聊天系统的前提下,商家客服为什么要迁移桌面应用?

首先我们收到部分商家客服反馈:

用户是上帝,我们是很重视用户的反馈的,所以首先我们想的是如何在web端解决这些问题,下面我们逐一分析下以上问题我们能不能在网页端解决呢?

1)针对客服A同学问题:大多数客服职场的台式机是不会安装音频设备,如果人家没音频,没外音,我们能强迫他买个播放器吗,那肯定是不能.,如果是自营客服还有点处理方案,真需要,公司可以统一采购,但是ToC的显然不能强制做什么事情,所以此问题无解.

2)针对客服B同学问题:这句话怎么理解呢?是这样的,在一屏既有web浏览器,又有其他应用如飞书之类的PC应用叠着放,web notification通知由于是浏览器级别的,提醒不到最上层,浏览器的提醒确实有点弱(看不到提醒会影响到客服的首响,首响会影响客服的绩效,咱公司对于用户的服务效率是比较严格的),所以此问题无解。

3)针对客服C同学问题:em。。。好的,您说的对,web网页给商家客服的感觉就是我们平台有点赶不上形势。

基于上面的一些场景,想必大家已经对为何做桌面应用有个初步的了解。下面以一张图来看下Web应用跟桌面应用的区别。

四、技术选型

为什么会选择Electron而不是其他应用开发框架?

4.1、Electron架构简介

Electron的构成主要是上面的3个大模块,每个模块各司其职,让Electron有了桌面应用的能力。

逐个理解Electron的3个大模块:

  • 1)Chromium:可以为Electron提供UI渲染能力,再加上Chromium本身就是跨平台的,所以也不用考虑代码的兼容性(只要有Chromium,就能用JavaScript,HTML,CSS这些前端开发工程师所熟知的三剑客来开发页面);
  • 2)Node.js:Chromium并不具备原生GUI操作的能力,所以Node.js正好补足了这个能力(能够调用操作系统的底层 API,比如path、fs、crypto 这些模块,甚至能集成C++);
  • 3)Native APIs:Native API让Electron有了跨平台和桌面端的原生能力(比如说它有统一的原生界面,窗口、托盘这些)。

4.2、Electron与其他框架的区别

下面是Electron与Native、QT、NW应用的对比图:

如上图所示:

  • 1)Native(C++/C#/Objective-C)不管从原生体验、包的体积、性能方面来说都是最佳的选择,但是开发门槛高、迭代速度慢;
  • 2)QT是基于C++的跨平台开发框架,跨平台应用十分广泛(Mac、Windows、ios、Android、Linux、嵌入式),众所周知的WPS就是用QT开发的。性能很好,甚至于可以媲美原生的体验,但是整体门槛还是比较高的;
  • 3)Web技术的代表Electron 和 NW.js ,相比之前选择Electron,Electron有非常活跃的社区(有102k star),有Atom、vscode这样的大型应用都是基于Electron开发的,性能相比于natvie是肯定要差一些,但综合来看,Electron是作为开发桌面应用的目前首选;
  • 4)值得一提的是Flutter desktop,从渲染原理看flutter是skia自绘性能优于Electron,但问题还是稳定性和生态。Electron由于是nodejs+chromium,前端的生态可以直接用,所以Flutter desktop就不纳入考虑范围的,但值得关注。

除了以上这些,最主要的一点:Electron能快速交付,业务层复用web系统的代码,只需要关注主进程就好了,并且也能满足业务需要的系统级别的一些功能。

五、技术实现

5.1、项目架构

首先介绍下Electron框架里面两个重要的概念主进程和渲染进程。

  • 1)主进程:主要负责创建和管理BrowserWindow实例以及应用程序事件。它可以执行注册全局快捷方式,创建系统菜单和对话框,响应自动更新事件等操作(主进程以及所有Node.js模块中都提供了一部分Electron API);
  • 2)渲染进程:渲染过程负责运行应用程序的用户界面,渲染进程中提供了所有DOM API、Node.js API和Electron API的子集。

如上面截图:打开Electron项目之后会有多个进程,一个项目有且只有一个主进程,创建窗口等有关系统事件写在主进程中进行,但是渲染进程可能有多个。

那为什么会有多个渲染进程呢?

Electron应用是Chromium内核,所以多进程的架构也来源于Chromium,Chromium会单独运行每个标签,任何一个标签页崩溃了都不会影响到其他标签。因此,每个进程在自己的空间中运行,由操作系统调度。如果某个进程触发了无限循环,也不会导致整个应用down掉。

在上文说到两个字“迁移”:是的,我们在开发桌面应用之前有非常成熟的web端商家客服聊天系统了,所以我们在开发桌面应用的时候大多数时候是关心主进程的,渲染进程并不太需要关心。

5.2、主进程功能模块

5.2.1通信模块

主要是调用Electron框架本身的API以及通用方法的封装:

5.2.1.1)主进程到渲染进程的通信:

5.2.1.2)渲染进程到主进程的通信:

有两种方案。

方案一:是在主进程开启了nodeIntegration: true之后在渲染进程里面可以使用window.require(\'Electron\')来引入写通信相关代码:

方案二:是需要在主进程编写preload.js(在初始化窗口的时候引入):

5.2.1.3)通信的同步和异步问题:

异步:渲染进程->发送->主进程

同步:渲染进程->发送->主进程

5.2.2菜单模块

主要是调用Electron框架本身的API,满足快速扩展菜单功能以及自定义菜单功能:

但是商家客服项目没用到原生菜单,而是用了自定义的菜单。

没有使用原生菜单的主要原因是:

  • 1)原生菜单样式较死板,不能调整;
  • 2)自定义编写菜单能有各种自己想要的功能,可控制其展示。

5.2.3 托盘模块

托盘属于系统级的操作,所以是在主进程中设置的。在开始之前需要注意的地方,设置托盘必须在程序ready之后。

实现较简单,代码如下:

5.2.4 异常处理模块

主要调用Electron框架本身API,结合Node.js API,检测系统异常后自动刷新并上报,渲染进程的异常已经使用sls&arms处理,主进程的异常主要是通过Electron的crashReporter API来记录日志。

上面提交参数有两个需要注意的点:

  • 1)submitURL 是以post方式上传;
  • 2)extra 一个你可以定义的对象,附带在崩溃报告上一起发送(只有字符串属性可以被正确发送,不支持嵌套对象)。

5.3、渲染进程功能模块

渲染进程的代码大部分跟商家客服IM web端一致,很多只是迁移即可。

5.3.1登录改造

登录信息本地化,在登录成功的时候,把账号信息缓存,下次打开应用的时候客服无需再重新输入,直接从缓存获取即可。

逻辑如下图所示:

5.3.2静态资源

传统Web应用,将项目代码部署服务器,项目运行时,访问的是服务器静态资源,现在版本发布流程,走的是cdn资源,总而言之都是通过网络获取。

Electron提供将静态资源打包到安装程序,在安装时,将项目文件同步安装到用户电脑,使其具备访问本地文件,减少了请求占用资源。一定程度上也能改善因网速问题导致的静态资源不能实时获取,页面白屏问题。

5.3.3 数据存储

Electron应用里面的数据存储是通过Electron-store第三方库来实现的。

实现比较简单,如下:

5.3.4 渲染进程打包

这块为什么要单拎出来讲渲染进程打包呢,是因为web项目迁移变成应用渲染进程的时候不能像web应用一样直接打包,需要调整请求API代码,API前缀需要区分本地调试和应用环境。

代码如下:

使用Electron, 将项目打包成离线应用。使用file协议,在本地读取静态资源。但是ajax请求如果用相对路径,打包之后,会直接找到根目录。如下截图。

所以打包的时候需要给ajax提供完整的url路径。

六、技术挑战

6.1、概述

我们在从0到1搭建商家客服IM桌面端的过程中,遇到了很多的问题。

原因是Electron社区虽然很活跃,但是不一样场景遇到的问题,几乎找不到对应的解决方案,所以很多都是在探索过程中不断的去完善。

这里主要围绕发布构建流程和安全性来讲下,我们是怎么解决的。

6.2、安全性问题

Electron客户端的安全问题也是非常重要的,那都遇到了哪些安全问题以及我们又是如何解决的呢?

具体如下:

  • 1)渲染进程XSS:Electron实现的桌面端软件渲染层的原理实际是通过chrome内核渲染的,同样存在XSS注入的风险(举个例子:在html页面中可以执行命令: ,就可以打开当前操作系统的计算器。接入了公司统一的XSS治理方案,该问题即得到解决);
  • 2)用户认证信息泄漏问题:商家客服桌面端登录调用商家的授权接口,APP网关有校验,可以确保登陆没有问题;
  • 3)本地缓存明文读取问题:本地数据泄漏(例如:indexDB、localStorage、sessionStorage等),我们主要用加密和解密算法对本地缓存信息进行处理。

没有绝对的安全,我们能做的就是尽可能的提高安全门槛,过程中我们也积极同公司的安全部门进行沟通,让他们排查桌面端发布之后的安全漏洞,最终验证都是满足安全标准,符合发布的条件。

6.3、发布构建流程

应用发布涉及到渲染进程和主进程,渲染进程主要是负责给主进程提供渲染包,主进程使用Electron-builder库来打包部署所发布的包。

前面已经说过:Electron的好处是可以无缝集成web端的业务逻辑代码,这里上图左边红色的是web端构建出的产物,我们会把这部分构建产物同步到主进程的app/render目录下(即渲染进程目录),这样在打包应用包的时候,就能集成渲染进程的业务逻辑,而不需要维护两份web端的代码。

此方案还存在不少的缺陷:由于生产构建环境需要window环境,所以暂时不支持在远程打包,目前都是在本地window机器上打出完整的包之后再上传到CDN,商家客服通过加载CDN的更新包来替换本地安装文件,实现软件的本地安装。

6.4、应用更新问题

应用开发离不开“更新”这个话题,比如飞书应用会时不时弹出一个更新窗口,让你选择是否更新。我们的商家客服IM在推广桌面应用之后,也存在更新这个问题。

在业务快速发展的同时,如何将业务需求更好的同步给商家使用,这是商家客服桌面应用面临的最大的挑战。

6.4.1全量更新:手动下载安装

这是最基础的更新模式,主要思路是在打开app(其他时候也行,我们业务主要是打开app的时候)的时候访问远程的json文件,文件内容包含版本号,更新内容等等最新版本的信息,拿到远程版本号会跟本地应用版本号做比较,如果版本号不一致,就询问用户是否更新,需要更新的话会下载到本地,用户手动点击安装即可。

这个更新方式不推荐使用,如果你的应用一年更新一次,ok,是可以这么做的。

6.4.2增量更新

在网速快的情况下,全量更新跟增量更新几乎是没有区别的。但是网速慢的情况下它俩之间的差距会被放大,用户体验不是很好。

我们不能想当然的以为所有用户网速都很好,这是不现实的,所以不管是PC应用还是移动端应用,大多数情况下是需要做增量更新。

下面表格是网速不一样情况下的下载耗时对比:

6.4.3增量更新方案1:electron-updater

现在就开始介绍我们在商家客服IM应用(windows应用)中是怎么实现增量更新功能的。

更新在大的分类上区分全量以及增量更新,在每个小分类里面也区分强更新、弱更新(业务上的区分,底层实现没区别)。

简单来说:

  • 1)强更新指的是用户必须更新,不更新将无法使用系统功能;
  • 2)弱更新指的是用户想要的时候再去触发应用的更新,完全由用户自主选择。

更新流程:

其中electron-updater作用于“更新应用”这个节点,主要是依赖新旧版本blockmap文件的对比来实现增量更新。

下图为electron-builder打包出来的release包,每次打包都会有对应的blockmap文件。

electron-updater更新实现主要流程分两大步。

生产的blockmap文件:

  • 1)使用7z压缩安装包;
  • 2)读取安装包的header;
  • 3)计算出每个file的offset和end得到相应的hash生产blockmap。

使用blockmap文件:

  • 1)下载云上的blockmap文件跟本地blockmap文件对比(从上面截图可以看出blockmap文件很小,所以下载并不会对应用性能产生影响);
  • 2)使用range,request(范围请求)请求更新内容的部分。

6.4.4增量更新方案2:文件替换

还有一种增量更新方式就是文件替换,只更新需要更新的模块。

这种方式只更新需要渲染进程的资源,大部分情况下主进程的资源不用更改,所以下载的资源会比较小,更新较快。因为是在线热更新,更新完成后不用重新启动软件,只需要刷新页面重新加载资源即可。

其实之前我觉的这样的思路挺好的,看下面的流程图也是可以实现的,也很符合商家客服桌面应用产品需求。

可是后来发现其实忽略了以下两个点:

  • 1)替换用户本地文件这个本身有权限问题(比如windows用户安装到了C盘,写入文件是有管理员权限限制的);
  • 2)文件被占用问题(众所周知,当文件夹中存在正在被占用的文件时,删除会失败)。

所以在覆盖原文件同时需要退出应用避免占用,所以这个方式也不是很可靠。

七、遇到的问题

我们在基于Electron开发客服IM桌面端的过程无疑遇到了很多问题,我拣主要的几个问题分享一下。

问题一:Electron 的硬件加速功能,在 win7 或者 Linux 系统上,容易出现黑屏或者卡死:

解决方案:判断是不是win7及以下系统,如果是app.disableHardwareAcceleration(),禁用当前应用程序的硬件加速。

问题二:“Uncaught ReferenceError: require is not defined”:

这个报错是试图在渲染进程使用node的时候出现的,不是不能用,只要开启 主进程的nodeIntegration: true就好了, 但不建议(有安全问题)。

解决方案:

问题三:“Note:you may have one or two (large) stale temporary file(s) left in your temporary directory (Generally this only happens on Windows 9x)”:

这个是打包了半天都打不出来一个完整的包的情况下出现的。

解决方案:当时是因为我没删除原来的包导致我放打包文件的C盘满了。所以删除一些缓存就好了,nsis打包大概率都是跟磁盘有关。

问题四:下载npm包特别慢:

解决方案:yarn安装、Electron相关的包优先使用淘宝镜像安装、使用公司镜像安装公司内部包。

八、本文小结

一路开发下来,感慨很多。

作为公司第一个Electron应用,不管是在开发上,打包上,或者说在部署上,都遇到了一些挑战。

在网上也没有比较详细的文档,外面做的好的也不会把详细方案分享出来。但是即使遇到了这些问题,也不能否认Electron是目前最适配于我们业务目标以及适配于开发资源的一个框架。

目前我们已有线上稳定版本,也逐步在推广到全部商家客服。

接下来需要完善的开发流程,克服的技术难点有很多,商家客服工作台应用也会越来越完善。

九、参考资料

[1] Electron官方开发者手册

[2] 快速了解新一代跨平台桌面技术——Electron

[3] Electron初体验(快速开始、跨进程通信、打包、踩坑等)

[4] Electron 基础入门 简单明了,看完啥都懂了

[5] vivo的Electron技术栈选型、全方位实践总结

[6] 融云基于Electron的IM跨平台SDK改造实践总结

[7] 闲鱼IM基于Flutter的移动端跨端改造实践

[8] 网易云信基于Electron的IM消息全文检索技术实践

 

学习交流:

- 移动端IM开发入门文章:《新手入门一篇就够:从零开发移动端IM

- 开源IM框架源码:https://github.com/JackJiang2011/MobileIMSDK备用地址点此

(本文已同步发布于:http://www.52im.net/thread-4159-1-1.html

 

以上是关于IM开发干货分享:IM客户端不同版本兼容运行的技术思路和实践总结的主要内容,如果未能解决你的问题,请参考以下文章

IM序列8:基于集群的移动端IM接入层负载均衡方案分享

Web端即时通讯技术原理分享

分享im即时通讯开发之WebSocket:概念原理易错常识

IM跨平台技术学习:得物基于Electron开发客服IM桌面端的技术实践

从零开始开发IM(即时通讯)服务端

im即时通讯开发:IM通信协议设计详解