Electron 在线热升级方案

一个项目正式交付客户使用了,对于版本的升级和代码更新是个一直困扰的问题,由于之前一直都是内部人员使用,可以让他们每次都下载完整压缩包(压缩后大概 40-50MB),但对于正式的终端用户,这个体验是不可接受。
参考过 Electron 官方文档,里面提及的方案兼容性并不好(Linux 不支持),需要准备的东西不少,而且交互上也不满意;以及参考了这篇 《我是如何实现electron的在线升级热更新功能的?》,最终按照里面的思路为该项目做了一个在线热升级的方案,本文介绍方案的细节及中间遇到的一些坑。

流程图

processon

关键步骤

  1. 通过 app.getVersion() 获取到 App 的本地版本号,这个版本号来源于 package.json
  2. 获取线上的 App 版本信息,此处我们设计了两个版本信息(如图例子);
    2.1. “1.3.0” 为核心版本号,该版本号用于判断是否需要重新下载完整包(图中所示的对应不同平台有不同的下载地址,为 zip 扩展名的压缩包,至于为什么用 zip,下文会提及),比如当 Electron 核心版本升级,或者增加了 node_modules 依赖等情况;
    2.2. “1.3.3” 为热更新版本号,这种情况只需要更新特定的文件列表(图中所示的 upgrade_1.3.3.tar.gz),通常包含 package.jsonsrc/* 等;
    2.3. description 分别表示核心版本和热更新版本的描述;
  3. 对比本地版本号 (appVersion) 和线上的版本号 (onlineVersion),下面是版本号比较的代码:

    3.1. 对比本地版本号和线上核心版本号 (onlineVersion.version),如果小于线上版本号,则激活浏览器并进入下载,同时退出 App;
    3.2. 否则进行对比本地版本号和热更新版本号 (onlineVersion.upgrade.version),如果小于线上版本号,则下载热更新包;
  4. 根据 (upgrade.url) 下载热更新包、升级、重启 App,全部代码:

  1. Windows 下可以直接把更新文件保存到 App 目录(没有权限问题),而在 Mac OSX 下,App 目录是无权写入的,而临时文件路径必须通过 app.getPath() 来获取,常用 temp 或者 userData 目录来保存解压文件;
  2. 压缩包我使用了 zip 一个原因是 Windows 下默认就能支持 zip 包解压,另一个主要原因是文件名编码问题,最初用 tar 来压缩文件,对于英文文件名是正常的,对于中文文件名,在 Mac OSX 下打包后在 Windows 是无法被正确解开的。而貌似 7z 生成的 zip 包会保存文件名字符集并能被 Windows 正确解开,所以在 gulp 中使用了 7z 来产生 zip 压缩包:

    而对于热更新包,由于源代码都是使用英文名,所以用 tar 来压缩没毛病:

webpack 怎样生成 inline (内联) 的 style 或 script

背景

最近一个项目用到 React,默认配置下,样式和脚本都通过混淆压缩合并后生成一个 css 或 js 文件(通过 ExtractTextPlugin ),并插入到 index.html 中,详细的配置可查看 eject 后的 webpack 配置文件。

而由于输出的 css 和 js 文件都有数十 KB,在第一次加载页面时,会出现一个白屏的情况,所以我在 index.html 中加了一个 Loading 效果,但是由于 Loading 的样式也是在 css 文件内的,并没有达到预期的效果,所以想是不是可以把一些基础样式单独抽出来放到 index.html 中内联。

ExtractTextPlugin

这个插件可以把符合条件的内容输出到一个独立的文件当中,详细配置查看 https://doc.webpack-china.org/plugins/extract-text-webpack-plugin/,在 webpack.config.prod.js 配置中已经包含了该 plugin,仅用于输出外部引入的 css 文件,我们可以修改下。

此处定义了两个实例,把 extractCss 用于处理原有的 css 文件输出,extractInlineCss 用于处理需要 inline 的 css,下面是常见的 webpack 配置内容,我们在 css 处理器前加上 inline css 的处理

完成这个步骤后,build 过程会对 inline.css 结尾的文件合并并输出到 main.inline.css

HtmlWebpackInlineSourcePlugin

这个插件可以把符合条件的 css 或 js 内联到 index.html 中,详细配置查看 https://github.com/DustinJackson/html-webpack-inline-source-plugin,这里我们需要把 inline.css 结尾的文件内联进来。

最后,build 完的结果是把这些 inline.css 的内容都内潜入 index.html 中,以致在未加载完独立 css 和 js 时也可以把 Loading 显示正确。

JavaScript 中获取函数调用者所在的文件名

在 BPF 服务框架中,服务间调用都通过 core 来做中转,需要知道调用者是属于哪个服务的,因此有了下面的代码,但不仅仅用于 BPF 中。

Discourse 通知系统的设计与实现

最近在为一个论坛开发一套站内的消息通知系统,参考过 Coding.net 和 知乎 的消息机制,以及简书上 《消息系统设计与实现》 的系列文章,下面我的设计和具体实现内容。

消息类型

系统中包含三种消息类型,本文只针对前两种通知类型,关于私信的详细内容请访问《Discourse 私信系统的设计与实现》。

  • 系统通知
  • 动态提醒
    1. 我关注的内容
    2. 我关注的用户
  • 私信

用户订阅

考虑到数据量规模,系统使用拉方式按需拉取用户订阅的通知。

用户和内容动态,包含三个基础属性

行为 action
对象类型 entityType
对象 ID entityId

比如您发表了一个主题,那么您会订阅该主题的[回复]、[点赞]、[投票]等行为;您发表了一个回复,可能仅仅只会订阅该回复是否有被[点赞]就可以了。

数据库设计

系统通知与动态提醒在数据结构上是一致的。

通知系统 ERD

  • users
    原有系统的用户信息表,主键 uid
  • users_follows
    用户关系表,保存用户与用户之间的关注关系
  • notifications
    通知信息表,每一条系统通知和用户动态都在该表中保存一行
  • users_subscriptions
    用户订阅表,用户所关注主题或回复的行为,如 关注了某某主题的评论、关注了某某回复的点赞
  • users_notifications
    用户与通知关联表,保存用户与通知的关系以及已读状态
  • users_notifications_updated
    用户通知拉取时间表,记录最近一次用户的拉取时间

 业务流程

  • 订阅和通知生成
    通知系统流程图1
  • 系统通知和用户关注动态拉取
    process2

代码实现

  • 订阅
    • 函数原型
    • 关键 SQL
    • 例子
  • 新通知
    • 函数原型
    • 关键 SQL
    • 例子
  • 获取拉取时间
    • 关键 SQL
  • 拉取系统通知
    • 关键 SQL
  • 拉取关注的对象动态
    • 关键 SQL
  • 拉取关注的用户动态
    • 关键 SQL
  • 更新拉取时间
    • 关键 SQL

CKEditor 增加自定义标签

在开发 Discourse 的投票功能时,使用到一个自定义标签 <poll> 以方便 PHP 识别帖子正文中的投票区块。
在 CKEditor 中,可通过修改 HTML DTD 中相关定义来完成 tag 添加,API 参考:http://docs.ckeditor.com/#!/api/CKEDITOR.dtd

JavaScript 定义 Object 内的常量

JavaScript 中,对于直接在当前作用域下定义常量,可以使用非 ECMAScript 5 标准的 const 关键字,而对于 Object 内定义常量,不能使用以下语句:

会提示语法错误 Uncaught SyntaxError: Unexpected token .

在 Node.js 中,可以使用 Object.defineProperty / Object.defineProperties 来实现 Object 内常量的定义,如下代码:

由于这两个方法属于 ECMAScript 5 标准,在 IE 低版本下会出现不兼容情况,所以在浏览器环境还是慎用。

Express 4 下使用 body-parser 报警告的解决方法

今天升级 Express 到 4.x,发现直接使用 body-parser 会提示 deprecated 警告

app.use(bodyParser());

提示如下:

body-parser deprecated bodyParser: use individual json/urlencoded middlewares app.js:14:9
body-parser deprecated undefined extended: provide extended option node_modules/body-parser/index.js:85:29

该警告在 Express 3.x 下不会发生,查找后发现在 stackoverflow 已有朋友发出解决方法拉:

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));