多数据库SaaS尝试

    手上的APIS要上SaaS,其实就是ASP(Application Service Provider)。要求一份程序,一或多份数据库跑服务。设计的时候参考了阿里软件人写的《互联网时代的软件革命-SaaS架构设计》。这本书从广度上覆盖了做SaaS的很多内容,但深度却显不足,很多地方只是浅尝辄止,一笔带过。

    虽然如此,作为算是这个领域的第一本入门书,还是有一些参考价值的。比如,定义了SaaS四个成熟度模型:定制开发、可配置、高性能的多租户架构、可伸缩的多租户架构;APIS只有一个运行实例,但租户数量不会很多,短期内不会超过50,所以算入第3级,高性能的多租户架构。

    在数据库设计上,这本书对SaaS应用,提供了四个可选方案。独立数据库、共享数据库+隔离数据架构(Schema)、共享数据库+共享数据架构(Schema)。由于采用了MySQL,没有schema的概念,所以去掉了中间的选项。虽然技术人员有上高精尖技术的冲动,不过鉴于我们的用户数不会很多,用户和用户之间也完全隔离,采用了第一种方案。而这种方案,对开发其他功能完全没有任何影响,算是非常unobstructive(中文不好翻译,一般叫“无侵入”)的方案。只要在入口的Filter留个ThreadLocal标志,最底层的SessionFactory做个拦截,功能上算是搞定了。大概就是事先准备好几个SessionFactory,然后哪个用户上来了就给谁相应的SessionFactory。

    但革命的道路是曲折的,过程是漫长的。虽然前期通过阅读Spring代码做过一些技术调查,但Spring+Hibernate这对组合的复杂性还是折腾我老半天。Spring对Hibernate支持很好,几乎是全方位的,这也造成了我要到处扩展用到的Spring支持类。本来只打算对LocalSessionFactoryBean动手,却忘了spring大部分的Bean都是singleton,但又舍不得换成prototype后的性能下降,于是抡起袖子又扩展了BasicDataSource(DBCP)、HibernateTemplate、HibernateDaoSupport、HibernateTransactionManager。好不容易有点眉目,开始从配置的租户数据库载入数据,却屡屡抛session提前关闭的异常。而且诡异的是,这个异常出现的概率约为75%,而一旦我用Debug跟进去后,反而基本不抛异常了。这让我异常胸闷,要知道这种问题十有八九是并发的原因,这问题找起来很头大,往往陷入Spring+Hibernate的汪洋大海。

    接下来几天感冒发烧,好好休息了一阵子,思路却打开了。从网上重新搜出一堆资料。这个是Stack Overflow的一个问答,几个回答都比较靠谱;这个是通过HotSwappableTargetSource提供的DataSource动态替换;这个是Spring开发人员利用AbstractRoutingDataSource提供对DataSource的路由。权衡再三,决定使用AbstractRoutingDataSource。只扩展了两个类就成功实现功能,不抛异常。全局一个SessionFactory,后面几个DataSource轮流上。

    但问题又来了。二级缓存这个东西和SessionFactory绑定,多个DataSource要求要多个Cache,因为各个租户之间的数据主键很可能重复。我试图搜寻通过改造SessionFactory的CacheProvider来搞定,但未果,SessionFactory的Cache埋得有点深。

   最后,参考了这篇这篇,用一个简单的Decorator,退回了多SessionFactory,虽然代码不是很好看,但再无异常。今天收工。