JAVA架构师打造微服务电商项目系统从设计到实现

面试被问如何设计一套电商系统,简单想象一下,既然是一个电商系统,有用户去购买,就肯定得有一个用户模块,购买什么东西总不是西北风吧,购买肯定是商品吧,省掉购物车,就得有商品模块吧。

商品总得有库存吧,库存就暂时跟商品放一起吧,什么仓储物流先别管,就当作是虚拟商品好了,反正题目也没说不能是虚拟商品。_

购买成功了,那就必须有订单吧,加个订单模块,下完单总得支付吧,不付钱人家凭什么把东西给你,那就得有个支付模块。

 
image

简单粗暴,四个模块,如上图:

  • 用户模块

  • 商品模块(库存)

  • 订单模块

  • 支付模块

好,几个模块搞定,外加下单流程图:

 
image
 
image

等等,貌似题目说是微服务,既然是微服务就涉及到拆分服务的问题。

DDD 领域驱动设计

刚刚确实是梳理了一下模块,既然是微服务,就得进行服务的拆分,服务怎么进行拆分呢?

貌似按照刚次梳理模块来划分也是可以的,不过这样好像显得我很是不专业,听说现在很多人都要使用 DDD(领域驱动设计)来指导微服务的拆分。

 
image

参考 DDD 的设计,DDD 官方的架构草图,总体架构分为四层:

  • Infrastructure(基础实施层)

  • Domain(领域层)

  • Application(应用层)

  • Interfaces(表示层,也叫用户界面层或是接口层)

微服务结合 DDD

不过对于领域设计而言,代码层其实不是最重要,最重要的是如何去划分领域,划分好边界。

而对于微服务而言,非常适合从业务上去划分各个 Modules,划分好各个业务板块,微服务 + DDD,个人觉得首先从微服务的角度考虑去划分大的业务模块,每个微服务都应该是一个可以独立部署,各司其职的模块。

简单的说,在微服务实际的开发中,结合 DDD 的思想去划分所有属于自己的领域。

实施 DDD 的关键

第一点是使用通过的语言建立所有的聚合,实体,值对象。

第二点也就是最关键的“建模”:

  • 划分“战略建模”,从一种宏观的角度去审核整个项目,划分出“界限上下文”,形成具有上帝视角的“上下文映射图”。

  • 还有一个建模是“战术建模”,在我们的“战略建模”划分出来的“界限上下文”中进行“聚合”,“实体”,“值对象”,并按照模块分组。

构建电商系统的上下文映射图

先来确定我们的战略核心的领域是什么?我们的目的是什么?

作为一个电商系统,我们的核心肯定是卖出更多的商品,获取更多订单更多的利润,那么销售可以作为我们的一个核心的领域。

这个作为一个明确核心域确立下来:

 
image

确定完核心子域后,根据对这个领域的理解划分出各个上下文,然后根据上下文再确定其他的相关领域。

 
image

初步我们可以看出围绕销售核心域的包含的几大块内容,价格,销售方式,购买的方式,已经购买。

然后我们对支撑着核心域的子域也做了划分,支撑着核心域的有商品域,用户域,通用域有订单域,物流域,支付域。

回到我们的主题,我们这次没有购物车,也没有各个会员销售价格,把一些上下文拿掉,并建立映射。

 
image

领域驱动设计看似简单,其实很难实施,因为在各个环节中都需要对应的领域专家的参加或指导,这样才能设计出最符合实际的上下文映射图。

而且我们花费的精力可能相比以后的数据驱动开发模式更多,但在整体对项目的把控性能上说,领域比数据驱动更加抽象,更加的顶层设计,在对应互联网的多变情况看得更远。

我们将微服务拆分为 5 个领域,分别是:

  • 销售域

  • 商品域

  • 用户域

  • 订单域

  • 支付域

完美,接下来就可以开始开发了。^ _ ^

 
image

等等,兵马未动,粮草先行;代码未动,图先行,先把时序图画出来。

时序图

一个简单的下单流程,涵盖了几个领域:

 
image

完美,接下来就可以开发微服务了。^ _ ^

 
image

等等,微服务的技术栈还未选型。

微服务技术栈选型

服务拆分完了,时序图也画完了,可以开始我们的微服务之旅了,目前主流的微服务有阿里大名鼎鼎的 Dubbo 和 Spring Cloud 全家桶,还有新浪的 Motan。

比较熟悉的还是 Dubbo 和 Spring Cloud,也都使用过,究竟应该选用哪一个呢?

因为之前都使用过,做点简单,粗暴的总结。Dubbo 在很早之前就开始使用,当时的微服务还没有现在这么火,很多理论体系也未完善,Dubbo 更像是一套 RPC 整合框架,Spring Cloud 则更倾向微服务架构的生态。

相比 Dubbo,Spring Cloud 可以说是微服务一整套的解决方案,在功能上是 Dubbo 的一个超级。

Dubbo 和 Spring Cloud 比喻,Dubbo 架构的微服务就像组装电脑,各个环节自由度很高。Spring Cloud 更像品牌机。

基于不折腾,简单快捷,更倾向选择 Spring Cloud。OK,就定下来技术栈使用 Spring Cloud,愉快的决定。

 
image

等等,就这么草率就决定用 Spring Cloud 做为微服务,难道不需要把微服务的利弊先弄清楚吗?

微服务的利和弊

既然选择了微服务,就得知道微服务的利和弊,特别是弊,引入了微服务,就等于引入了一套复杂的体系,一套复杂的体系带来的各种挑战必须事先了解清楚。

 
image

①强模块化边界

我们知道做软件架构,软件设计,模块化是非常重要的一点,一开始我们写程序做软件,我们采用类的方式来做模块化,后面开始采用组件或类库的方式做模块化,可以做到工程上的重用和分享给其他团队来使用。

微服务在组件的层次上面又高了一层,以服务的方式来做模块化,每个团队独立开始和维护自己的服务,有明显的一个边界。

开发完一个服务,其他团队可以直接调用这个服务,不需要像组件通过 Jar 或源码的方式去进行分享,所以微服务的边界是比较清晰的。

②可独立部署
③技术多样性

弊(或者说挑战)

①分布式复杂性

在原来单块应用就是一个应用,一个对单块应用的架构比较熟悉的人可以对整个单块应用有一个很好的把控。

但是到了分布式系统,微服务化了以后可能涉及到的服务有好几十个,一些大公司可能涉及到的服务上百个,服务与服务之间是通过相互沟通来实现业务。

那么这个时候整个系统就变成非常复杂,一般的开发人员或一个团队都无法理解整个系统是如何工作的,这个就是分布式带来的复杂性。

②最终一致性

微服务的数据是分散式治理的,每个团队都有自己的数据源和数据拷贝,比方说团队 A 有订单数据,B 团队也有订单数据,团队 A 修改了订单数据是否应该同步给团队 B 的数据呢?

这里就涉及到数据一致性问题,如果没有很好的解决一致性问题,就可能造成数据的不一致,这个在业务上是不可以接受的。

③运维复杂性

以往的运维需要管理的是机器+单块的应用,分布式系统和单块应用不一样的是,分布式系统需要很多的服务,服务与服务之间相互协同。

那么对分布式系统的资源,容量规划,监控,对整个系统的可靠性稳定性都非常具备挑战的。

只有在清楚了解微服务带来的挑战,明知道山有虎偏向虎山行,才能够真正的胜任挑战,最重要的是,要清楚明了里面有什么坑,怎么避免踩坑。

完美,已经了解微服务带来的好处和挑战,接下来就可以开始开发了。^ _ ^

 
image

等等,微服务还没有做逻辑分层。

微服务怎么做逻辑分层

目前我们的微服务里面有几个服务,分别是订单,商品,用户。

如果客户端向查看 “我的订单” 这么一个接口;如果客户端假定是 PC 端,就需要请求三次接口,分别对接订单,商品,用户三个服务,分别拿完三次调用数据,再将三次调用数据进行整合输出展示。

要知道 PC 调用后端服务是走外网,这无疑大大增加了网络的开销,而且让 PC 端变成更为复杂。

假定在中间加多一个层为聚合服务层,即对网络开销进行减少,因为微服务内部是通过内网进行数据传输,也让 PC 端的业务变得比较简单。

 
image

图中的 “PC 聚合服务” 也是一个微服务,只不过它是属于聚合服务中间层,我们将为微服务进行逻辑划分,分为 2 个层:

 
image
①微服务基础服务层

基础服务一般属于互联网平台基础性的支撑服务,比方说,电商网站的基础服务有订单服务,商品服务,用户服务等。

这些都属于比较基础和原子性,下沉一个公司的基础设施的低层,向下承接存储,向上提供业务能力,有些公司叫基础服务,中间层服务,公共服务,Netflix 成为中间层服务。我们暂且统称为基础服务。

②微服务聚合服务层

已经有了基础服务能提供业务能力,为什么还需要聚合服务,因为我们有不同的接入端,如 App 和 H5,PC 等等,它们看似调用大致相同的数据,但其实存在很多差异。

例如 PC 需要展示更多信息,App 需要做信息裁剪等等。一般低层服务都是比较通用的,基础服务应该对外输出相对统一的服务,在抽象上做得比较好。

但是对不同的外界 App 和 PC 的接入,我们需要作出不同的适配,这个时候需要有一个层去做出聚合裁剪的工作。

例如一个商品详情在 PC 端展示和 App 端的展示,PC 可能会展示更多的信息,而 App 则需要对信息作出一些裁剪。

如果基础服务直接开放接口给到 PC 和 App,那么基础服务也需要去做成各种设配,这个很不利于基础服务的抽象。

所以我们在基础层之上加入聚合服务层,这个层可以针对 PC 和 App 做成适当的设配进行相应的裁剪。

那么我们的微服务中,又增加了一个服务,属于聚合服务。

 
image

好了,接下来可以愉快的 Coding...

 
image
 
image

等等,貌似不对,如果是单块应用加上事务应该没问题,这里是分布式,恐怕得考虑加分布式事务。

分布式事务

我们来理一理创建订单和扣件库存模块之间的关系:

 
image

可以发现,因为微服务的原因,我们把服务进行了分布式,随着各个数据库也随着变成分布式每个数据库不一定存在相同的物理机中。

那么这个时候单个数据库的 ACID 已经不能适应这种情况,而在这种集群中想去保证集群的 ACID 几乎很难达到,或者即使能达到那么效率和性能会大幅下降,最为关键的是再很难扩展新的分区了。

这个时候如果再追求集群的 ACID 会导致我们的系统变得很差,这时我们就需要引入一个新的理论原则来适应这种集群的情况,就是 CAP。

CAP 定理

CAP 必须满足以下的 3 个属性:

  • 一致性(C):在分布式系统中的所有数据备份,在同一时刻是否同样的值。(等同于所有节点访问同一份最新的数据副本)

  • 可用性(A):在集群中一部分节点故障后,集群整体是否还能响应客户端的读写请求。(对数据更新具备高可用性)

  • 分区容错性(P):以实际效果而言,分区相当于对通信的时限要求。系统如果不能在时限内达成数据一致性,就意味着发生了分区的情况,必须就当前操作在 C 和 A 之间做出选择。

简单的来说,在一个分布式系统中,最多能支持上面的两种属性。但显然既然是分布式注定我们是必然要进行分区,既然分区,我们就无法百分百避免分区的错误。因此,我们只能在一致性和可用性去作出选择。

在分布式系统中,我们往往追求的是可用性,它的重要性比一致性要高,那么如何实现高可用,这里又有一个理论,就是 BASE 理论,它给 CAP 理论做了进一步的扩充。

BASE 理论

BASE 理论指出:

  • Basically Available(基本可用)

  • Soft state(软状态)

  • Eventually consistent(最终一致性)

BASE 理论是对 CAP 中的一致性和可用性进行一个权衡的结果,理论的核心思想就是:我们无法做到强一致,但每个应用都可以根据自身的业务特点,采用适当的方式来使系统达到最终一致性。

好了,说了一大顿理论,程序员们都等急了,赶快来看看分布式事务的解决方案有哪些,可以进行接下去的 Coding...

来吧,讨论技术方案:

 
image

几个方案拿出来了,因为我们不是专门来讲解分布式事务的机制和原理,主要还是来做分布式事务的技术选型。

先排除掉我们应该不会选择的方案,一个是 XA 两阶段提交,这个在很多传统型公司会被使用,但不适合互联网微服务的分布式系统,锁定资源时间长,性能影响大,排除。

另一个是阿里的 GTS,并没有开源,目前已经开源了 Fescar,不过目前尚缺少调研,可能在下个阶段研究后会使用,目前先排除。

剩下的是 TCC 和 MQ 消息事务两种。

MQ 消息事务:RocketMQ



猜你喜欢

转载自www.cnblogs.com/java188/p/12124379.html