传统应用系统架构的瓶颈
曾经为物业管理公司架构设计并领导开发过一个互联网+物业增值服务平台项目,刚接手这个系统的时候,这个平台在最初的1.0版本采用的是比较标准的单体应用,Java、Tomcat、MySQL,功能服务也比较简单,主要还是物业报修相关,不过在我接手后,客户就开始了2.0的规划,这就包括了物业缴费、物业通知、线上礼品兑换,因此我们延续这个架构完成了2.0版本的上线。
如下图所示:从架构角度上看,两台Tomcat组成了两套互为备份的APP服务器,部署业务服务,形成服务冗余的可靠性保障,Nginx服务器作为均衡负载对两台APP服务器实现轮询访问,也能提升请求性能,最终由APP服务读写MySQL数据库。
2.0上线后系统一直表现稳定,不过已经出现了一些开发维护方面比较吃力的端倪,主要问题在于物业相关服务、支付相关服务、商品增值服务并不在一个领域,不同领域问题自然就会涉及更复杂的相关负责人协调,不同服务的更新发布节奏也不同,每次进行微量更新的时间点,都要向所有管理人员打招呼,有时候也会比较尴尬。
例如:我们近期接到了商业拓展部的需求,需要我们增加了一个积分活动,升级程序完成开发测试后,发布过程需要暂停服务15-30分钟,当我们通知到所有负责人后,这时候集团的物业管理公司的负责人就会问,我们最近没有啥新需求啊,咋又让我们给业主发通知呢?
不过还好,问题不算太大,PM用微笑能解决的,咱就不让程序员加班熬夜想解决办法了。
那么问题就来了,若平台再压上更重的稻草咋办?果然,系统应用磨合期刚过不久之后,客户就提出了3.0升级计划,其一:这次要将物业管理系统彻底推倒重来,与我们的互联网物业增值平台进行融合,其二:线上礼品兑换服务彻底升级为社区内线上零售系统,其三:支付系统对接银行系统,代理银行的金融增值产品。
从项目参与面的角度看,与平台项目交涉的客户方不仅物业管理公司人员全部纳入到使用者范围,而且又增加了银行客户、社区加盟商等。
作为架构师的我,遇到了极大的压力,若项目在3.0的升级设计上,还是延续原来的系统架构接着开发,那么这种错综复杂的关系就不可控了,这也是目前整个开发组的共识!
因此我必须开始尝试新的方案,微服务架构自然而然地出现在了我的视线范围,我亟待通过微服务架构的方式来解救日益临近悬崖的项目。
微服务架构的切分方法
解决微服务架构问题的关键还是如何进行整体平台的微服务化切分,形成大小适中,独立性强的可运行服务,微服务的切分方法模式告诉我们可以通过:操作端切分法、领域模型切分法、业务主线切分法、用户群体切分法、性能热点切分法等方法模式进行平台的微服务化划分,不过实际运用上不是非黑即白,必须基于实用性,架构上应具有包容度。
例如:我们平台运维过程中最关注的一项需求就是不同领域的业务负责人之间尽量不要因为系统升级维护相互影响,那么我们就可以重点考虑基于领域模型和操作端(管理端)混合的方式来切分,第二关注点在于平台架构受外部系统的影响尽量不要相互干扰,比如像支付系统、物业管理系统、物流平台、银行金融接口等,这些外部系统总是会与领域模型紧密结合。
基于上面两点的强烈关注,另外也要从微服务的粒度不能过细去考虑,否则我们作为一个小型团队,人手上不应一次性维护过多微服务,过多微服务又会导致更复杂的服务间交互所带来的升级维护成本。
因此我们粗粒度的划分为物业服务、支付金融、社区商业、平台资源四块微服务,那么不同微服务会负责关系密切的外部系统。如下图所示:
这种方式就完全让不同微服务针对到了不同的业务责任人,物业管理公司、银行、商业拓展部以及平台内部运维管理。
这种微服务架构的升级变迁所带来的技术支撑问题那就是数据库的切分、业务网关的引入和容器技术的使用。
数据库切分逻辑
首先是数据库的切分问题,如下图所示:
早期涉足微服务的架构师系统喜欢用单库这种思路来进行划分,这样微服务化的效率最高,不过恰恰是这种设计会导致微服务操作数据表的责任边界极为模糊。这就会逐渐形成一种危害——分布式环境的数据一致性要求。
例如:物业缴费调用支付模块,尽管跨越了两个微服务,但是程序员为了图省事,将所有支付返回的数据操作由物业缴费模块进行了数据库的写入,只不过通知支付金融微服务向银行系统做同步,但是物业模块恰恰在完成数据写入库的过程中,支付系统出现了故障,此时平台的数据库已经写入支付数据,但是无法通知支付金融微服务向银行系统同步数据,这就是数据不一致了。
若微服务对于数据库的操作,是完全从物理隔离上独立划分,那么这个操作就一定是在支付模块上完成,必然能保证微服务故障前后的数据一致性。
另外一个重要因素就是任意服务对数据库的访问压力都会导致整体服务的下降,反过来讲,若数据库保持独立,任意微服务出现故障,至少部分服务仍能继续维持,例如:物业报修并不会因为支付金融微服务崩溃而受到影响。
因此微服务应该越保持独立性越好!
因此我们需要针对数据库进行了划分,这就形成了下图中的微服务架构,我们可以看到基本上每个微服务都会对应属于自己的数据库。
这个数据层面的切分过程需要考虑两点:
(1)遗留数据库的迁移问题,一般有两种思路,一种是进行平滑迁移不影响系统的正常使用,类似于空中加油,另一种是一次性研发完成后,离线ETL,并做公测,公测完成后再统一替换。
第一种非常麻烦,主要是针对7×24小时在线的高可用性平台,例如:互联网电商、公共事业服务、互联网医疗系统等,一般也会用到大数据技术配合ETL来做。
不过我们还好,物业增值业务没有那么严苛的高可用性需求,我们选择了第二种方式。
(2) 数据切分之后,必然涉及数据事务操作从原来的单库事务转变成了目前多库的分布式事务,这块主要还是用到了TCC(Try/Confirm/Cancel)进行多个微服务的TCC,如下图所示:
我们在进行物业缴费过程中通过TCC框架对物业服务微服务和另外一个支付金融微服务的物业缴费模块、互联网支付模块适配了Try-Confirm-Cancel的编码规范,也就是进行了准备阶段-提交阶段的两阶段提交,这样就能基本保证物业缴费数据和支付数据的分布式一致性。
后续
前面提到过对于微服务的架构除了数据库的切分迁移之外,另外有两个部分:其一就是业务网关在微服务中起到的作用,其二就是容器技术,为什么容器技术在微服务中非常重要,就是因为容器对服务器资源进行了合理的隔离,形成了容器间相互独立的进程服务,这就极大促进了微服务的部署效果。这两部分的内容我再抽时间聊聊我在项目中的实战经验。