一、背景
我们在聊架构风格之前先明确一个问题,什么是架构们为什么要选择架构、用来解决哪些问题/p>
1、什么是架构
书本定义:“软件的架构是一种抽象的结构,他由软件的各个组成部分和这些部分之间的依赖关系构成”。我的理解是,架构就是根据业务选择合适的技术、中间件,并且按照合适的设计模式对这些模块,进行组装来满足业务特性的需求。
2、选择架构风格的目的
我们选择架构风格的初衷在于 “三更原则”(自己的理解) : 更好地降本提效、更快地发版上线、更好地维护系统稳定性。
任何一个架构风格,都可以实现功能性需求,但是一个好的架构风格能在功能性需求之上,提升非功能性需求,那么你可能会问,什么是非功能性需求例:扩展性、稳定性等等。
这里我将会以我认知结合踩过的坑,来给大家详细讲一下,我们是如何从单体架构演进到分布式架构,在向分布式单体架构的演进的道路上,又如何进行的抉择,以及为什么最后同时选择了微服务架构+分布式架构的原因。接下来就结合一个系统来作为案例,贯穿主线讲解。
首先来讲一下,最初的单体架构的经历和转型。
二、单体架构
我们在系统创建之初,往往都是集中业务、单点部署系统,所有业务打一个包,快速上线。满足了业务初期的快速发版上线,而且适合中小公司没有自己的paas平台,应对初期快速迭代的业务,开发、迭代、测试、发布都是非常的便捷。那么单体架构都有什么类型呢/p>
1、单体架构的类型
单体架构也分为大泥团架构、分层单体架构、模块化单体架构,他们的区别是什么呢/p>
1)大泥团单体架构: 毫无分层、所有模块聚焦在一起,相互穿插(除非是你接手需要改造,否则不要创建这样的架构风格,这种大泥团架构很难拆分,到最后的下场往往都是重新搭建)。
2)分层单体架构: 普遍的选择,架构进行了简单的分层,比如传统的mvc三层架构。
3)模块化单体架构: 一般是随着业务的发展,由分层单体架构演变而来,特点就是引入了多个业务模块并且提供相应的服务能力。
2、单体架构的优缺点
1)单体架构的优点
-
应用的开发很简单
-
易于对应用程序大规模的更改
-
测试相对简单、直观
-
部署简单明了
-
横向扩展不费吹灰之力
在业务的初期,单体架构的优点,无论从哪个方面来说,都优于其他架构风格,但是随着业务的增加、耦合,单体架构的缺点也逐渐暴露出来,这个也符合“康威定律”。那么单体架构的“后期”会暴露出哪些问题呢/p>
2)单体架构的缺点
-
代码库膨胀
-
过度的复杂性会吓退开发者
-
开发速度慢
-
从代码提交到实际部署的周期很长,而且容易出问题
-
难以扩展
-
系统的稳定性得不到保障
-
需要长期依赖某个可能过时的技术栈
单体架构的这些缺点,其实影响的还是我上面提到的“三更原则”。经过上面的铺垫,相信大家已经对单体架构风格已经有了简单的理解,那么光有方法论是不行的,我们得结合项目以及代码片段来加深理解,做到真正的应用。
接下来我就用一个库存系统来进行串联进行讲解。先通过这张图来了解下库存系统是用来做什么的/p>
-
创建之初,1个服务提供商品库存维护、库存查询、库存扣减能力。
-
随着业务的发展,库存面向多个服务:B端业务,平台内部业务系统、平台外部中台。C端业务,订单商品扣减库存、 关查询库存数量。
在最初很长的一段时间里,我们部署了两个单体服务,一个是API接口来保障上游的库存查询以及调用,另一个是web服务的后台管理平台。这两个单体服务很好的贴合了最初的业务迭代和发版速度,但是后来随着业务的增加附加调用量的增加,单体服务的无论是从性能和稳定性都出现了较大的波动。
4、意料之外,情理之中的事故惨案
2015年6月26日晚,也是一个促销活动的前夕,库存的web管理平台挂了,原因就是大量库存导入,服务器的内存不足导致机器宕机。商家、运营无法通过导表的方式去维护库存数量,在这之前已经经历过了多次横向扩容。还是出现了预料之外的流量和稳定性的问题。
拆分后,不同的服务应对不同的业务方,系统错误的隔离性好,不会说出现一损俱损的局面,稳定性上也有了保障。在解决了稳定性的问题后,留给我们了一些喘气的间隔,可以有时间去进行代码的优化。因为刚才也提到了,我们只是通过分布式的集群部署来解决容错性的问题,但是代码还是一套,臃肿的代码也会拖慢我们的开发上线速度。那么接下来要进行的就是,对业务代码的解耦,这块也是难度最高的。我们是如何做的呢/p>
2)业务拆分
业务拆分的思路是什么呢/p>
-
以业务本身为导向,充分了解系统业务模型,划分业务边界
-
业务依赖的范围,细分功能,尽量减少功能之间的重复依赖
-
根据拆分功能的影响大小进行评估,拆小保大
-
拆分的过程中不要修改业务逻辑,不要进行拆分之外的任何优化动作(除非是bug)
基于上述拆分的思路,库存系统又是如何划分的业务模块呢了哪些代码/p>
3)如何划分业务模块
关于业务划分, 上有很多方法论,事件风暴法、四色建模法等等,但是万法不离其宗,那就是围绕事件。 以库存系统举例:库存初始化(门店+sku库存创建)、库存数量维护(修改现货数量、修改可售状态)、扣减业务(购物车扣减、提单扣减、订单取消扣减)、提醒业务(缺货提醒)等。每一个事件都有独立的链路轴,以及时间线可以形成闭环。
这里截取部分代码片段作为案例,来讲述下我们在拆分业务的过程中,需要做一些什么操作。
-
对service层进行CQS的拆分
-
把业务逻辑从原有的service层抽离,保障service方法遵循SRP原则。
-
新增业务聚合层(或者向六边形架构里提到的adapter转接口)来聚合service层的方法
- Read服务
③抽离到业务层business层后
④构建好的业务层
1)优先拆分读还是优先拆分写
建议从拆分读开始,因为读服务相对于写服务简单一些,而且更容易提高系统对外服务的稳定性,写服务的流程相对底层改动比较大,测试的周期也会比较长。在前期,动写服务系统出问题的概率会比较大,所以综合稳定性、扩展性来说,优先拆分读服务是一个比较好的选择。
2)CQRS的思想适合所有业务场景吗
以库存系统举例,我们就按照CQRS的思想复刻一版,看看会出现什么问题。
-
每一次修改同步库存写入任务表
-
schedule任务读取任务表
-
把任务表的修改数据同步到Read服务中的redis中
在这个过程中,存在两个问题:
-
大数据量任务同步的问题。也就是Event Bus同步redis的数据同步速度问题。
-
延迟问题。库存要求实时性非常高,如果因为任务积压导致的延迟,会让库存陷入困境之中。大量的库存数量不对导致的超卖、超卖会瞬间击溃业务。
库存通过这套设计强依赖了Redis来作为库存查询、修改的中间件。保障了数据的强一致性。库存在原有的服务上,分离了读写,保障了系统的CQRS命令职责查询分离。
声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!