早想着要写一篇博客,但由于各种原因(其实因为懒),迟迟没有动笔。今日下决心,写写关于软件服务架构的一点感悟。
三层架构
从读大学开始,老师就讲三层架构。后来的项目实施基本上也都是三层架构。对于小型项目,业务逻辑相对简单的项目,三层架构是快速迭代的利器。随着项目的迭代,功能越来越多,业务逻辑越来越复杂,业务开发团队越来越庞大,单体的三层架构就逐渐暴露出它的不足。因为这样的项目是一个高内聚、高耦合的项目,一个类、一个方法可能被多处引用,给维护带来了极大的不方便,要修改一个方法、修改一个字段,可能会影响到所有引用它的方法。如果项目中还存在包引用、dll 引用,可能还会导致包名冲突、命名空间冲突。这种情况下,我们会去想,如何降低业务复杂度?答案是拆分服务,微服务化。开发团队的壮大对于高效的管理也是一个问题,微服务化后,原先业务团队被拆分成多个微服务团队,也降低了管理的难度。
微服务架构
图 1
图 2
对于微服务如何划分,粒度多粗多细,每个团队有每个团队的划分法。从我个人的经历来看,无外乎一种细粒度划分,一种粗粒度划分。
图 1,展示了细粒度的服务划分通常的一个结构。对于一个复杂的业务,如果服务划分过细(这样的服务通常绝对禁止跨库访问),业务逻辑层必然要对服务进行组装,不管是 RPC 的调用方式,还是 Rest 的方式,此时的业务逻辑层仍然是一个高内聚、高耦合的模块。对于一个需要快速迭代的产品,这样的架构快速不起来。比如一个下单服务,业务逻辑层的负责团队需要等待商品服务相应的接口、订单服务相应的接口、库存服务相应的接口等下单涉及到的接口准备就绪,才能开始编写服务。
图 2,展示课粗粒度的服务划分通常的结构。这时,一个微服务接口是一个粗粒度的接口,微服务与微服务之间不允许相互调用,而允许跨库访问,降低了服务之间的依赖,这样的的微服务是一个高内聚、低耦合的模块。还是以下单服务来举例,此时负责订单服务的团队,编写服务的时候,不必等待商品团队、库存团队、用户团队(收货地址)等其他团队,他们自己可以快速着手开发下单服务。负责库存服务的团队,只写他们关心的服务,比如商品的采购、入库。而订单团队需要的库存扣减操作接口,订单团队自给自足。(这里引申一个问题,从 DDD 的思想来看,一个 Domain 的边界,到底在哪里?拿库存来说,一切有关库存的操作都写到这个 Domain 叫做 DDD?如果只有订单模块会扣减库存,退单后会加回库存,这样的接口还写在库存服务模块?这类接口属于订单领域还是库存领域?)
- 本文地址:AIQ - 架构 | 软件服务架构的一些感悟
- 本文版权归作者和AIQ共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出
微服务,不仅能够降低业务复杂度、开发团队管理难度,而且由于微服务的特性,使得部署软件的资源能更合理高效的应用,降低资源成本。
高并发
图 3
软件并发量逐渐提高,不管是三层架构、还是微服务,优化的途径都差不多,读写分离-》加缓存-》分库分表。上方所示图 2 到图 3,展示了利用一些数据访问中间件(Sharding-JDBC、Macat、Atlas&&)实现分库分表的架构。
重构
对于一个潜在的可能存在高并发场景的项目,如何能在遇到高并发的时候,从容地重构一个项目?我有一些浅见,供大家讨论:
(临时搭建的项目,凑合看吧)
如上图所示,项目划分了两个 Package,一个 Product,一个 Membership,请注意,ProductController 里面引用了 Membership 这个 Package 里的 UserService,换句话说,就是 ProductController 依赖了 Membership 这个 Package 里的 UserService。假如我们约定,不允许跨 Package 依赖,那么 Product 这个 Package 里有需要用到 User 相关的服务的时候,自己写到 Product 这个 Package 里。那么当我要进行服务拆分的时候,只需要把 Product 这个 Package 单独打包成一个 jar,即可拆分完成。这样约定,方便从三层架构重构到上图 2 的架构,对将来可能的分库分表没有任何影响。
如果业务进一步复杂,采用图 2 所示架构也会遇到问题。比如库存数据,如果除了订单服务之外还有其他的服务都去修改库存数据的话,在没有统一日志或者这样的服务数量比较多的情况下,想要知道这个库存数据是怎么来的,会非常困难。这个时候只能重构成图 1 的架构。采用图 1 的架构,需要业务专家来定义接口,使其尽可能地适应多的使用场景。
注意:本文归作者所有,未经作者允许,不得转载