聊聊架构演化
0. 前言
一般来说,技术架构和业务架构都是随着业务需求变化而逐渐演化的。每一步演化都因为某一个业务场景推动且有明确目标。下面我将用一个现实工作中的案例讲解这种演化过程。我会着重讲解关键变迁的原因及改造之后的带来的效果。
1. 单体应用阶段
图1
这是某学网最初的业务架构和技术架构图。刚开始的时候业务非常简单,所以架构也非常简单。为了方便后续讲解,我先来介绍一下核心业务。首先网站的使用这是学校的老师和学生,老师登录网站挑选合适的试题(录入试题)组成一张试卷并标注标准答案。线下组合学生考试。考试完成后将学生答题卡通过扫描仪上传到网站,网站可以解析学生答题卡和标准答案比对,自动对客观题进行评判,然后给出分数。学生可以查看分数。
业务并不复杂,只有三个主要模块:
用户模块:任何系统都有的模块,存储教师和学生的用户数据。
题库模块:存储题目信息。
考试模块:存储试卷信息,负责组卷,判卷,查看试卷分数等功能。
整体架构也不复杂,整个应用部署于一个Tomcat集群中,集群前使用Nginx负责负载均衡和反向代理服务。数据库使用MySQL主从结构。
2. 分布式服务阶段
随着公司业务的扩展,用户访问量成线性递增趋势,单体应用通过扩展集群中机器数也可以很好满足日益增加的访问需求。但是促使我们队业务进行分布式改造的不是非功能性需求,而是业务的快速变化带来的功能性需求递增。
随着业务变得越来越复杂,单体应用的代码量越来越大,不同团队成员在这个巨大的工程中工作起来越来越困难,为了不相互干扰,新建了大量的分支,安排上线计划需要协调各个团队,且每次上线都有大量的分支需要合并。
业务复杂也造成了大量的代码耦合,一个接口的变动可能涉及到很多团队修改。且单体应用中没有太多物理隔离,团队之间的代码边界很难规范。
因为以上的问题,我们必须将单体应用按业务先拆分。
(图2)
图2是按照业务拆分后的架构图。
业务代码完全拆分,各个团队独立维护,单独上线,需求响应更加灵活。
模块间通过接口调用,规范业务范围划分。
将数据库拆分,达到更好的数据隔离性,避免跨模块越级访问问题。
随着业务进一步扩展,不断的有新的业务模式产生,也就不断的有新都模块和相应的开发团队加入到系统中。随之发现了一个问题,每个模块都有以下业务是公共的,例如鉴权、认证、日志、监控的,新增的业务模块要么自己重新实现。于是进行了进一步改造。
变化点:
新增了网关服务,统一负责的认证、鉴权、防爬、限流和熔断服务。
新增了监控平台,统一各模块监控数据采集流程。
新增日志平台,统一各模块日志格式及日志采集流程。
成立基础平台团队,负责各模块团队都需要使用的中间件和组件的维护工作。
至此我们完成了分布式架构的改造工作。新增模块团队只需专注于自身功能性需求,同质化的功能性和非功能性需求有其他团队统一实现并维护。
最终,业务拆分后,为我们带来的了两点明显好处:
业务解耦:团队可并行工作,快速响应业务变化。
技术解耦:开发按照需选择技术栈,根据各自业务特点制定技术方案。这个后面就会介绍。
3. 存储架构重构
分布式改造之后,业务有序且平稳的进行着。随用户请求增加,我可以通过适当添加某个模块集群节点进行横向扩展保障用户响应。但是随着存储数据数量的递增,我们又遇到数据库的瓶颈,不得不让我们将存储优化改造提上议事日程。
场景
题库模块负责试题创建、查看、关联、知识点分析等相关业务,题目的多少和优劣是教育类网站的核心竞争力。最初题目的来源有两个方面,内部上传,教师上传。因为都是人工录入,数据增加比较缓慢,试题总量也不多。后期公司为了提升自己的题目数量,增加竞争力,向集团申请兄弟公司试题资源,我们定期同步数据。从人工到自动的转变使我们每日新增题目也成百上千倍增加。数据库压力也与日俱增。
问题
数据库暴露了两个主要问题:
随着存储量增加,查询效率越来越低,即使是索引查询也无法满足我们需求。
业务中有很多对题目like的需求查询效率也是每况愈下。
虽然问题2直接原因不在于数据量,但是随数据量增长类似于like的解决方案弊端也暴露了出来。所以我们想一次性解决。
解决方案
初步想法
关于数据量存储:
虽然题目递增数量比较快,但是我们预估最终全科题目总量量在100m左右。所以我们最终的存储量是可以预估的,且大概率准确的。
当前我们的存储在MySQL中,解决数据库问题可以纵向升级或横向扩展,我们首先否定了纵向升级,因为当时分库分表的技术方案已经比较成熟了,纵向升级的性价比就不高了。而且我们对于题目存储的业务场景相对简单(没有事务需求和复杂查询),规避了分库分表后雷点。
关于全文检索:
业界也有比较成熟的方案(例如ElasticSearch),兄弟部门也有使用,集成进来的成本也不高。
解决方案:
对MySQL进行分布式改造
引入ES为我们提供全文检索服务。
(图3)
让我们再来思考一下图3方案,这里有一个比较明显的问题,我们将一份数据存储了两遍,只是为了满足不同的使用场景。那数据能否存储一处而满足两个使用场景呢?
显然存储于MySQL无法满足我们对象存储的需求。那么都存储于ES中呢?对于题库模块我们没有事务需求,没有聚合需求,只有简单的索引查询和全文检索需求。且数据插入、更新频率也不是很高,和ES使用场景比较贴合。
最终方案
(图4)
最终我们就是采用ES一种存储方式。上线之前将原有MySQL数据一次性导入ES即可。
4. 后语
上文描述了两次架构改进,一次是因为业务耦合影响团队快速响应需求变化,另一次是因为存储数据量激增影响查询效率。其实从根本上讲都是因为业务需求的变化而造成的架构调整。
通过两次改造我们再思考两个问题:
先知先觉
为什么我们一上来使用单体应用架构?而不是直接使用分布式架构,毕竟一开始只有三个模块,直接拆分也不复杂。
具体架构选择不能从后向前看,必须要考虑到当前的场景。
首先这些模块是在开发过程中逐渐清晰的,一个新的系统往往对照一个新的需求场景,业务场景是逐步梳理清晰。可是在互联网快速响应需求的背景下,我们往往不能等到需求完全清晰的时候就进入到开发阶段。而且一般初代产品都是为了验证业务需求,验证之后很可能对需求进行调整,所以我们不能不顾业务变动的可能性,一开始就按照开发人员的理解将模块固定化。
其次,时间和成本角度考虑,越简单的架构开发时间越短,需要人力越少。这两点对于项目初期非常重要。
对于MySQL的选择也是同样的考量,总之就是先快速实现。
但是还是要看场景,如果我们的业务就是复制市场上也有成熟业务,业务逻辑清晰,公司资金相对充裕,开发时间相对宽裕,我们完全可以一上来就采用使用相对复杂的架构和实现。
再接再厉
为什么我们没有进一步做分布式架构优化,打造一个中台系统?
这其实也是由业务场景决定的。
中台是为了将核心业务沉淀公用,方便上层应用快速衍生。
当前我们的业务没有这么复杂,而且模块之间耦合度也不高,业务模块界限相对比较清晰。新增业务横向演化即可。即使沉淀一些细粒度服务也很难模块外公用。
如果未来在各个横向业务模块之上再衍生出新的业务,产生了中台的试用场景,我们也会进行中台化改造。
综上所述,业务变化是架构演进变化的重要原因,我们应该致力于打造一个更加贴近业务的架构系统。
版权声明: 本文为 InfoQ 作者【Jerry Tse】的原创文章。
原文链接:【http://xie.infoq.cn/article/b407983ce1a1a40a43751a811】。文章转载请联系作者。
评论