Mid Station

没关系也没关系

计划着有时间就要陆续把Centaur完善起来了,虽说现在怎么看都只能算是一匹弱智矮脚马,有机会还是要往怪兽的方向发展一下。

也算是历史遗留问题,在为Centaur做架构选型的时候,因为有前面的烂尾项目铺路,就颇为放心地沿用了PostgreSQL来做数据库,心想搭配着SQLAlchemy应该也出不了什么乱子。到了上学期末心血来潮大刀阔斧地改架构适应灵活的脚本机制的时候,再用起这套黄金搭档就好像有些力不从心了。起初觉得是SQLAlchemy太过笨重,常常是翻了大半天文档也挤不出半行代码,而且大部分高级的功能也用不到。于是就移花接木般地改用了peewee这个库,果然是轻量级的ORM库,写起来也顺手多了。

又是为了适配自定义脚本的机制,由于每个扫描脚本返回结果的数据最好能灵活地反应数据本身的结构,这样传统用ORM定义字段就不太好使了,最好是能用JSON格式来保存结果。还好PostgreSQL在9.4版本加入了一个叫JSONB的字段类型,正好适用于这种灵活的数据场景,同时,翻遍文档挠破头皮的我还发现,JSONB字段正是从一种叫做NoSQL的玩意儿里面偷师过来的。以MongoDB为代表的NoSQL,中文译作非关系型数据库, 自然是为了和传统关系型数据库对着干才起了个这样大逆不道的名字。两者最大区别就在于NoSQL可以说省去了建表这样的操作,数据可以直接以松散的key-value方式存放在数据库中。

怀着能跑就好,先跑起来再说的唯物主义伟大观念,我选择暂时无视NoSQL,继续用着PostgreSQL。到了寒假接近结束的时候,适应了自定义脚本操作的Centaur架构上还是有着重大的缺陷。此时的脚本机制是这样的:Server接受了Client发送过来的HTTP包以后,通过Celery将任务分发到各个Worker,Worker执行完任务直接将结果发送到数据库。这就使得Worker获得了直接对数据库的操作权限,显然是不够安全的,而且还要在Worker上引用ORM对结果进行实例化再保存,增加了脚本逻辑的复杂度。新方案是在Worker完成任务后直接把结果通过RESTful API发送会服务器,再由服务器实例化后存入数据库。好像是增加了服务器的负担,但好处是能使各个部件能够隔离开来,逻辑也比较清晰。

随后继续为Centaur增添了几个新功能,尤其是用Flask搭建Web界面时,总是有种隔靴搔痒的感觉。Flask总是有很多方法帮我们实现想要的效果。比如导航栏、分页条,网上有很多现成的代码供我们参考,但是步骤都是要在前端用HTML先写好界面,再用Flask进行数据绑定渲染模板,搞不好还得用JavaScript添加几个动作。这样繁琐的步骤加上繁复的调试,项目烂尾的机率突然陡增。目前比较有吸引力的一个解决方案是采用AngularJS等框架实现前后端的终极分离。

也是处于这个考虑,即使有了JSONB的加持,PostgreSQL在这个场景中也称不上顺手,干脆就直接转向NoSQL使用MongoDB。当然这也是经过仔细考虑的,作为扫描系统,或者说爬虫一类的系统,出于一下特点不妨使用NoSQL

  1. 对事务的原子性要求不强。即使不能保证原子性,也不会产生损失(相对与金融系统)
  2. 保存的数据比较“脏”。比如说爬虫爬下来的HTTP响应包就挺合适的,因为由于服务器的差异,响应包经常是“缺胳膊少腿”的,HTTP头少字段,cookie包含空值等都经常碰到。NoSQL此时灵活的结构就很合适
  3. 不好为数据建表,或者说你懒得建表。

于是今天花了一个下午完成了转用MongoDB的代码重构,幸亏之前改变了Worker提交结果的方式,整个过程还算比较顺利。说到底还是没有脱离ORM,这次用了mongoengine这个库,忙完后对着代码发了会呆:既然都是用ORM,又何必转到MongoDB

事到如此,也没关系了。