前一段时间想对博客系统进行一次大面积更新,因为原有Nodejs后台很多地方做的不好,无论从设计上还是编码结构上都没有达到我想要的效果。所以国庆节前就开始着手对新系统进行设计,经过几天的重构修改,前台部分第一版已经完成,后续还会继续优化。

预计目标

我对我自己博客系统的构想一直没有过变化,而且就这个设想我还专门撰写过一篇名为《我想要一个怎样的Blog系统》。我对这次系统更新做了如下几个优化点。

  • 打开速度要更快
  • 依然不需要花哨功能
  • 路由不变
  • 对外显示优化

打开速度

打开速度主要取决于三个因素:

  1. 服务器网络延时
  2. 服务器业务逻辑处理耗时
  3. 前端页面渲染耗时

由于第一条和第三条对我来说实在没什么可做的,这次更新主要针对第二条进行。

关于那些花里胡哨的功能

我用过wordpress,也玩过discuz,甚至大大小小的网站系统都用过。但是,这些网站系统为了更佳灵活方便的添加功能和替换模板,导致访问速度大大下降,主要性能瓶颈来源于内部逻辑。例如,wordpress中设计很多钩子,这些钩子绝大部分用户系统插件和一些核心组件通信。虽然灵活度提升,却牺牲一部分性能。

与此同时,这些系统提供很多自定义选项,会导致数据库结构变得复杂。单一页面中数据源来源于数个不同的表。虽然存在缓存等机制,但当一部分数据更新后,会增大逻辑处理数量。

为了避免这些不必要的开销,我并没有在博客系统中设计这些好用的“钩子”。换句话说,这个博客系统除了本身的功能外,只能自己coding添加功能,甚至是在影像原有结构的基础上,再通俗一些讲,就是业务逻辑都被我“写死了”。

语言选择

expresskoa

思来想去,nodejs令我不爽的原因有三:

  1. 糟糕的包管理
  2. 过度的异步回调
  3. 内存无法精确控制
koa
  1. PHP
  2. Python
  3. Nodejs
  4. Golang

经过反复对比,最终我选择了golang,原因如下:

  1. 语言性能最优,远比nodejs和php快
  2. 部署方便,golang可以打包为一个独立可执行文件,这样部署就极为方便了。
  3. 轻量级线程处理,远比nodejs的异步使用起来舒服
  4. 语法糖更接近c,用起来舒服

最重要的一点在于,号称网络版c语言的golang在性能上存在绝对优势。

缓存设计

原有久系统中并没有使用缓存,这次更新添加redis缓存支持也是我的目标之一。在对nodejs添加这部分功能的过程中,我又一次感受到了回调函数的痛苦。

缓存的设计也是考虑了一部分性能的因素,事实上,我有两种缓存策略可以使用。

单页缓存

所谓单页缓存,就是将一张渲染好的网页进行gzip压缩后存入redis中。

这种策略优势在于存入的value体积小,是gzip之后的结果。同时,每次检查到存在缓存后,直接取出来发送给客户端即可,逻辑简单,速度快。

弊端也很明显,当网站的标题修改,或者分类有修改,所有缓存的网页都需要更新,也就是说,所有的缓存都会被废弃,需要重新生成。

模块数据缓存

一个网页中,主要分为四个模块数据,分别是:

  • 网站数据,例如网站title,描述等
  • 网站单页列表
  • 网站分类列表
  • 当前页面内容

我可以将这四个模块数据分别进行缓存,然后使用到的时候再将其组装为一个网页,而后进行gzip压缩,发送给客户端。

这种策略优势在于数据分开存放,互相独立。无论谁更新了数据,都互不影响,没有单页缓存策略的弊端问题。

而弊端在于,每一次访问页面,都需要四次redis读取,同时都要进行一次gzip,增大了io和cpu的压力。

选用方案

最终选用了单页缓存方案,虽然改变网站属性和分类都会导致所有缓存实效,但从业务逻辑上来考虑,网站属性,和分类这些内容,改变的几率非常小。不会经常变化,所以采用了这种方案。

静态文件策略

express

只需要在nginx中,做如下配置即可:

http {
  server {
     #静态文件由nginx处理
     location ~* \.(html|css|js|png|jpg|gif|ico)$ {
         root /srv/www;
     }
     #其他所有请求由go处理
     location / {
         proxy_pass http://localhost:8080;
     }
  }
}

这也是nginx非常经典的配置方法。

对比效果

我对服务器的网络进行了一系列的测试。由于服务器位于南方,北方访问ping值需要60到70ms左右的延时。也就是说,当我打开我博客任意一个页面的时候,时间都不可能小于60ms。这是由于物理网络限制的原因。所以在最终测试的时候,我会将实际时间减去60作为对比。

原有ndoejs服务支撑的博客系统,打开单页面,仅html文件,耗时都需要150ms左右。而切换到golang新系统后,耗时均在70ms到100ms之间。所有时间均小于100ms。这个速度已经达到我的预期效果。

也就是说,更换了golang之后,加之redis缓存的作用,在不使用并行计算的前提下,一个页面的逻辑之行时间在10ms到20ms之间。