先介绍一下Vcenter,Vcenter是我欲开发的一个完全的后台项目,由KOA驱动,届时会将我手头所有的网站资源都转移到Vcenter之中,将其作为一个完全的后台处理中心。

有句话说得好,过早的优化是万恶之源

框架开发了一个起点,我就迫不及待开始对它进行优化

优化起点1 SESSION持久化

一个优秀的框架肯定要支持 SESSION ,当然,也可以适度支持一下 SESSION 持久化,在Vcenter中,对于SESSION的需求主要体现在网站方面,但是仅仅对网站提供 SESSION 肯定不够完美,最好对微信公众号后台也支持 SESSION ,但是由于服务器仅对微信消息暴露一个接收消息的接口,无法从HTTP协议上区分,因此必须设计一个兼容的 SESSION 存储方案。

npm上面那个koa-session肯定是不太符合的,那个必须通过cookies来标记,于是只能自己手动开发。

session插件开发的几个问题都比较好解决,唯一的难点是session持久化。
在持久化方案上,我选择了Mysql,直接存储SESSION对象的JSON字符串的方式。

再根据db的要求,稍稍优化一下代码,最后每个连接仅访问两次数据库,一次空连接的TTFB从10ms被压缩到6ms。

优化起点2 DB单例

Vcenter作为一个中心服务器的话,高并发支持是肯定要有的,所以我在导入DB之后就开始了DB的并发测试。
工具是Autocannon,测试结果非常糟糕。

在一开始的编码设计中,我为每一个ctx上下文创建了一个 数据库连接,这样当并发数较高的时候,就直接报出

too many connections

这样肯定是不行的,数据库又撑不住,因此必须对数据库的连接进行优化。
在DB框架上,我选择了一个Medoo,在PHP上,Medoo表现尚可,因此在nodejs上,虽然这个开发者的上次维护已经在两年前,但是我还是选择了继续使用这个框架,无他,唯手熟尔。

在ctx上下文中,我将db的导入做成了一个函数,使其状态单例,这样,整个应用程序并发的会话都将使用同一个数据库连接。
这也将空接口的TTFB时间从6ms缩短到2.5ms。

优化起点3 DB预编译语句

数据库状态单例以后,整个处理速度提升了一大截,由于SESSION依赖数据库,因此优化数据库的好处是很多的。

再用一次Autoconnon,这一次调高并发数和测试时间。

不出意外,数据库又报错了,这一次报的错误是

 Can't create more than max_prepared_stmt_count statements

赶紧Google之,发现是预编译语句的问题,调用了太多的预编译语句而没有释放,导致了这个问题。
要解决很简单,调高Mysql的预编译语句上限即可,但是问题不在此,我才写了几句sql,当然不可能是Mysql的问题

Medoo本身使用的是node-mysql2,因此先去node-mysql2的issue查找,很快找到了类似的问题,但是该问题早在6年前就已被修复。
细查mysql2的文档库,其清楚的表明了,使用execute()函数可以防注入,原理是将其预编译,在预编译以后,字符串部分会使用一个简单的缓存存储下来,以便下次直接使用。

看到这里都没啥问题,没办法了,log调试,好在medoo本身支持debug模式,开了以后,会在execute之前将sql语句打印出来。

仔细观察了一下,终于找到了原因。
Medoo本身对语句进行了防注入处理,但是编译出来的sql表达式却并非是正常的预编译语句

Medoo :
execute(SELECT `value` FROM `session` WHERE `NSESSIONID` = '97507215cd067b1e4bcc9b6b420a491be13cef8e' AND `expires` > 1539583338076 LIMIT 1)

Mysql2 :
execute('SELECT * FROM `table` WHERE `name` = ? AND `age` > ?',['Rick C-137', 53])

在Medoo提交execute的时候,查询条件也作为了预编译的一部分,由于每次请求都会产生不同的语句,导致缓存策略失效,每次产生不同的预编译语句使Mysql的预编译语句达到上限,由此产生了大量的报错。

解决方案,首先Fork了该Medoo库,由于原作者已经两年没有维护了,我也不指望能够通过Issue继续沟通,Fork之后直接将execute改成query,反正都已经生成完整的sql语句了,直接通过query执行查询即可。

但是与此同时,也表明着这可能会带来一定的注入风险,于是拿出TodoList,记下一笔

完善修复数据库框架Medoo ,修复其预编译功能

结语

过早的优化带来的将会是无尽的坑。
但是这次优化也给我提了个醒,npm并非是一块金山,其上的package大多良莠不齐。
在引入一些包和框架的同时,勤于测试也是保证代码质量的重要方法之一,否则一股脑上线了以后,这边报一个错,那边并发出问题,任谁也受不了。

以上就是Vcenter三个提前优化的全部心得,附一个Autocannon的测试数据。
server端:单进程
测试方案:100并发 * 10秒 * 2 pipe

snipaste_20181015_141050.png

在低并发数的情况下,响应时间要更为优异一点