`

架构|微服务业务开发三个难题-拆分、事务、查询(转)

 
阅读更多

转自:http://mt.sohu.com/it/d20170517/141317853_470018.shtml

 

架构|微服务业务开发三个难题-拆分、事务、查询(上)

 

  微服务架构变得越来越流行了。它是模块化的一种方法。它把一整块应用拆分成一个个服务。它让团队在开发大型复杂的应用时更快地交付出高质量的软件。团队成员们可以轻松地接受到新技术,因为他们可以使用最新且推荐的技术栈来实现各自的服务。微服务架构也通过让每个服务都被部署在最佳状态的硬件上而改善了应用的扩展性。

  但微服务不是万能的。特别是在 领域模型、事务以及查询这几个地方,似乎总是不能适应拆分。或者说这几块也是微服务需要专门处理的地方,相对于过去的单体架构。

  在这篇文章中,我会描述一种开发微服务的方法,这个方法可以解决这些问题。主要是通过领域模型设计,也就是DDD以及事件源(Event Sourcing)以及CQRS。让我们首先来看看开发人员在开发微服务的时候会遇到哪些问题吧。

  微服务开发过程中的挑战

  模块化在开发大型复杂的应用的时候是非常有必要的。

  现在许多应用大到一个人根本无法完成。而且复杂到光靠一个人去理解是不可能的。

  这种情况下,应用就必须被拆分成一个个模块。在单体应用中,模块被定义为比方一个java package。然而,这种做法在实践中并不是很理想,时间长了,单体应用就变得越来越庞大。微服务架构把服务作为一个模块单元。

  每个服务对应一个业务能力,这个业务能力是组织为了创造价值而需要的。例如,基于微服务的在线商店包括各种服务,包括订购服务(Order Service),客户服务(Customer Service),目录服务(Catalog Service)。

  

  每个服务都有一个不可渗透且很难违反的边界。也就是每个微服务要提供一种单独而独立的能力。这样的话,应用程序的模块化就更容易随时间保存。

  微服务架构还有其他优点。包括独立地部署服务,独立地扩展服务等等这些能力。相比单体来说。

  不幸的是,拆分并没有听起来那么容易。相当难。

  应用的领域模型,事务,查询这三个东西就是拆分过程中和拆分后你所面临的拆分难题。让我们来看看具体原因吧。

  问题1 – 拆分领域模型

  领域模型模式是实现复杂业务逻辑的一种非常好的方式。比如针对一个在线商店,领域模型将会包含这么几个类: Order, OrderLineItem, Customer 和 Product。在微服务架构中,Order和OrderLineItem类是Order Service的一部分;Customer是Customer Service的一部分;Product属于Catalog Service的一部分。

  

  )

  拆分领域模型的挑战之一就是class们通常会引用一个或多个其他类。

  比如,Order类引用了该订单的客户Customer;OrderLineItem引用了该订单所订产品Product。

  对于这些想要横跨服务边界的引用,我们该怎么办呢?

  稍后你将会看到一个来自领域模型设计的概念:聚合(Aggregate)。我们通过聚合来解决这个问题。

  微服务和数据库

  微服务架构的一个非常明显的功能就是一个服务所拥有的数据只能通过这个服务的API来访问。

  在一个电商网站中,比如,OrderService占有一个数据库,里边有一张表ORDERS;CustomerService也有自己的数据库包含表CUSTOMERS。

  通过这样的封装,微服务之间就解耦了。

  在开发期间,开发人员可以独立修改自己服务的数据库shema而不需要与其他服务的开发协调勾兑。

  在生产上,服务之间都是隔离的。比如,一个服务从来不会因为另外一个服务占有了数据库的锁而导致阻塞等待。

  不幸的是,这种数据库的拆分让管理数据的一致性以及不同服务间跨表查询变得困难。

  问题2 – 跨服务分布式事务实现

  一个传统的单体应用可以通过ACID事务来强制业务规则从而实现一致性。

  想象一下,比如,电商里的用户都有信用额度,就是在创建订单之前必须先看信用如何。

  应用程序必须确保潜在的多个并发尝试去创建订单不超过客户的信用限额。

  如果Orders和Customers都在同一个库中,那么就可以使用ACID事务来搞定:

  BEGIN TRANSACTION…SELECT ORDER_TOTAL FROM ORDERS WHERE CUSTOMER_ID = ?…SELECT CREDIT_LIMITFROM CUSTOMERS WHERE CUSTOMER_ID = ?…INSERT INTO ORDERS ……COMMIT TRANSACTION

  不幸的是,在微服务架构中我们无法通过这种方式管理数据的一致性。

  ORDERS和CUSTOMERS表被不同的服务所拥有,只能通过各自的服务API访问。他们甚至可能在不同的数据库。

  一种比较常见的做法就是使用分布式事务来搞定,比如2PC等。但是这种做法对于现代应用来说也许不是一种可行的方案。CAP定理要求你必须在可用性和一致性之间选择,可用性通常是较好的选择。

  而且,许多现代技术,例如大多数NoSQL数据库,甚至不支持ACID事务,更不用说2PC。

  所以管理数据的一致性需要使用其他的方式。

  稍后你将会看到我们使用事件驱动架构中的一种技术叫事件源(event sourcing)来解决分布式事务。

  问题3 -查询

  管理数据一致性不是唯一的挑战。还有一个问题就是查询问题。

  在传统的单体应用中,我们通常使用join来实现跨表查询。

  比如,我们可以通过下面的sql轻松的查询出最近客户所订的大额订单:

  SELECT *

  FROM CUSTOMER c, ORDER o

  WHERE

  c.id = o.ID

  AND o.ORDER_TOTAL > 100000

  AND o.STATE = ‘SHIPPED’

  AND c.CREATION_DATE > ?

  但我们无法在微服务架构中实现这样的查询。

  就像前面提到的那样,ORDERS与CUSTOMERS表分属不同的服务,只能通过服务API来访问。

  而且他们可能使用了不同的数据库。

  而且,即使你使用事件源(Event Sourcing )处理查询问题可能更麻烦。

  稍后,你将会学习到一种解决方案就是通过一种叫CQRS(Command Query Responsibility Segregation)做法来解决分布式查询问题。

  但首先,让我们看看领域驱动设计(DDD)这个工具,在我们的微服务架构下基于领域模型开发业务逻辑是必要的。

  DDD聚合是微服务的构建块

  像你看到的那样,为了使用微服务架构成功的开发业务应用,我们必须去解决上面所说的那些问题。

  这几个问题的解决办法你可以去Eric Evans的书Domain-Driven Design中找得到。

  这本书,是2003年出版的,主要介绍了设计复杂软件的一些方法。这些方法对开发微服务也同样有用。

  尤其是领域驱动设计可以让你创建一个模块化的领域模型,这个领域模型可以被多个微服务所使用。

  什么是聚合?

  在领域驱动设计中,Evans为领域模型定义了几个构建块。

  许多已经成为日常开发人员语言的一部分,包括entity,就是指一个具有唯一标识的持久化对象。value object,也就是VO,你经常听说的,是用来存放数据的,可以与数据库表对应,也可以不对应,有点类似用来传输数据的DTO。service,就是指包含业务逻辑的服务。但不应归类到entity或者value object。

  repository,表示一堆entity 的集合就是一个repository。

  构建块(building block),聚合(aggregate)常常被开发人员忽略,除了那些DDD爱好者,或者叫“狂热分子”。

  然而,聚合(aggregate)被证明是开发微服务的关键,非常重要。

  一个聚合(aggregate)就是一组domain的集合,可以被当作一个单元来处理。这里说的一个单元就是可以当做原子来处理。

  它包含了一个root entity以及可能还有一到多个关联的entity以及value object。

  比如,针对一个在线商店的domain model就会有几个聚合,比如Order和Customer。

  Order聚合又由一个root entity Order和一个以上的OrderLineItem value object组成,而且OrderLineItem还有可能关联有其他vo,比如快递地址(Address)以及支付账户信息PaymentInformation。

  Customer聚合又由一个root entity Customer和其他的vo比如DeliveryInfo 和PaymentInformation组成。

  

  )

  使用聚合将领域模型(domain model)分散和参与到每个聚合中,这也使得领域模型更容易理解了。这也同时厘清了操作的scope,比如查询操作和删除操作等。

  一个聚合通常作为一个整体被从数据库中load出来。删除一个聚合,也就是删除了里边所有的object。

  然而,聚合的好处远远超出了模块化一个领域模型。 这是因为聚合必须遵守一定的规则。

  聚合之间的引用必须使用主键

  第一个规则就是聚合通过id(例如主键)来引用而不是通过对象引用 。

  比如,Order通过customerId来引用Customer,而不是引用Customer的对象。

  类似的,OrderLineItem通过productId来引用Product。

  

  )

  这种做法与传统的object modeling非常的不同。虽然后者认为通过外键引用在领域模型中这样做看起来怪怪的。

  通过使用ID而不是object引用,意味着聚合是松耦合。你可以轻松地把不同的聚合放在不同的service。

  事实上,一个微服务的业务逻辑是由一个领域模型组成。这个领域模型是几个聚合的一个组合。比如,OrderService包含了Customer聚合。

  一个事务只创建或更新一个聚合

  第二个规则就是聚合必须遵循一个事务只能对一个聚合进行创建或更新。

  当我第一次看这些规则的时候,当时并没有什么感觉。因为那时候,我还在开发传统的单体应用,那种基于RDBMS的应用。所以事务可以更新任何的数据。今天,这些约束依然适用于微服务架构。它确保一个事务只被包含在一个微服务中。此约束还符合大多数NoSQL数据库的有限事务模型。

  当开发一个领域模型,一个很重要的事情就是你必须确定每个聚合得搞多大。

  一方面,聚合理想情况下应该是小的。它通过分离关注点来改善模块化。

  这是更有效的,因为聚合通常被全部加载。

  此外,由于对每个聚合的更新是顺序发生的,因此使用细粒度聚合将增加应用程序可以处理的并发请求数,从而提高可扩展性。

  它还将改善用户体验,因为它降低了两个用户尝试更新同一聚合的可能性。

  另一方面,因为聚合是事务的范围,您可能需要定义一个较大的聚合,以使特定的更新原子化。

  例如,之前我描述了在在线商店领域模型中,Order和Customer是独立的聚合。

  另一种设计可以是把Orders作为Customer聚合的一部分。

  一个较大的Customer聚合的好处就是应用可以强制对于信用额度进行原子验证。这种方法的缺点是它将订单和客户管理功能组合到同一服务中。这也降低了可扩展性,因为更新同一客户的不同订单的事务将被顺序化。

  类似的,两个用户去尝试编辑同一个客户下的不同订单有可能会冲突。而且,随着订单数量的增加,加载一个Customer聚合的成本也会变得更昂贵。

  由于这些问题,尽可能的把聚合细粒度是最好的。

  即使一个事务只能创建和更新一个单独的聚合,微服务应用中也依然必须去管理聚合之间的一致性。

  在Order服务中必须验证一个新建的Order聚合将不超过Customer聚合的信用额度。

  这里有两种不同的解决一致性的方法。

  一个做法就是在单个事务中欺骗的创建和/或更新多个聚合。这种做法的前提是,所有的聚合都被一个服务所拥有并且这些聚合都被持久保存在同一个RDBMS中才有可能。

  另一个做法就是使用最终一致的事件驱动(event-driven)方法来维护聚合之间的一致性。

  使用事件驱动来维护数据一致性

  在现代应用中,对事务有各种约束,这使得难以在服务之间维持数据一致性。

  每个服务都有自己的私有的数据,这时候2PC的方案就变得不可行了。

  更重要的是,很多的应用使用的是NoSQL数据库,这些数据库根本就不支持本地ACID事务,更不用说分布式事务了。

  因此,现代应用程序必须使用事件驱动的,最终一致的事务模型。

  什么是事件(Event)

  根据 Merriam-Webster(一个单词网站),事件的意思就是: something that happens:

  

  )

  在本文中,我们将领域事件定义为聚合发生的事件。一个事件(event)通常表示一个状态的改变。现在还是拿电商系统举例,一个Order聚合。其状态更改事件包括订单已创建(Order Created),订单已取消(Order Cancelled),订单已下达(Order Shipped)。事件可以表示违反业务规则的动作,如客户(Customer)的信用额度。

  使用Event-Driven架构

  服务们使用事件来管理聚合之间的一致性,像下面这样的一个场景:一个聚合发布事件,比如,这个聚合的状态改变或者一次违反业务规则的尝试等等。

  其它聚合订阅这个事件,然后负责更新他们自己的状态。

  在线商店制创建一个订单(order)的时候验证客户(customer)信用额度使用下面一系列步骤:

  1. 一个订单(Order)聚合创建,并且状态为NEW,发布一个OrderCreated 事件。

      2.客户(Customer)消费这个OrderCreated事件,然后保存为这个订单保存信用值然后发布一个CreditReserved事件。

      3.订单(Order)聚合消费CreditReserved事件,然后修改自己的状态为APPROVED。

      如果信用检查由于资金不足而失败,则客户(Customer)聚合发布CreditLimitExceeded事件。

  这个事件不对应于一个状态的改变,而是表示一次违反业务规则的失败尝试。 订单(Order)聚合消费这个事件后,并将自己的状态更改为CANCELLED。

  微服务架构可以比作事件驱动聚合的Web

  在这个架构下,每个服务的业务逻辑都是由一个或多个聚合组成。

  一个事务只能包含一个服务,并且是更新或创建一个单独的聚合。也就是聚合内事务。

  服务们通过使用事件管理聚合之间的一致性。

  

  )

  这种做法一个非常明显的好处就是一个个聚合变成了松散而解耦的构建块。

  他们可以被作为单体应用来部署或者作为一组服务来部署。

  这种情况下,在一个project开始的时候,你可以使用单体架构。

  之后,随着应用的体积和开发团队的规模的扩大,你就可以很容易的切换到微服务架构上来。

  总结

  微服务架构从功能上把一整个应用拆分成了一个个服务,每个服务又都对应一个业务能力。当我们开发基于微服务架构的业务应用的时候,一个关键的挑战就是事务、领域模型以及查询,这三个主要的麻烦都是拆分之后所带来的问题。你可以通过使用DDD聚合的概念来拆分领域模型。每个服务的业务逻辑是一个领域模型,然后这个领域模型是由一个或多个DDD聚合组成。

  在每个服务中,一个事务只能创建或更新一个单独的聚合。由于2PC对于现代应用来说并不是一个可行的解决方案,所以我们需要使用事件机制来去实现聚合之间的一致性(以及服务之间)。在下一集,我们会描述使用event sourcing来实现一个事件驱动的架构。我们也会向你展示在微服务架构下通过使用CQRS来实现查询。

  关于作者:Chris Richardson是一位开发人员和建筑师。 他是Java Champion和POJO in Action的作者,他描述了如何使用Spring和Hibernate等框架构建企业Java应用程序。 Chris也是原始CloudFoundry.com的创始人。 他与组织协商,改进他们如何开发和部署应用程序,并在他的第三个创业公司工作。 你可以在Twitter @crichardson和Eventuate上找到Chris。

分享到:
评论

相关推荐

    微服务业务开发三个难题-拆分、事务、查询

    微服务与事件源(Event Sourcing)为了应对微服务架构中的事务难题,一种可行的解决方案是采用事件源(Event Sourcing)。事件源是一种存储系统状态的方式,它记录所有导致当前状态的事件,而不是直接存储当前状态。...

    从业务架构到微服务

    微服务架构是一种设计思路,旨在将一个大型的应用分解成一系列小型、独立的服务,每个服务都围绕着特定的业务能力构建,并且可以独立地部署、扩展和维护。这种架构方式不仅提高了系统的可维护性,还增强了团队的开发...

    微服务架构下分布式事务解决方案-阿里GTS.docx

    微服务架构下分布式事务解决方案的主要困难有三个方面:第一,单体应用拆分为分布式系统后,进程间的通讯机制和故障处理措施变的更加复杂。第二,系统微服务化后,一个看似简单的功能,内部可能需要调用多个服务并...

    分布式事务难题在微服务架构下的解法和标准化1

    分布式事务在微服务架构下是实现复杂业务逻辑的关键技术,因为微服务架构通常涉及多个独立的服务,这些服务可能需要在单个操作中保持数据的一致性。然而,由于服务之间的网络延迟、服务失败等问题,传统的单体应用中...

    微服务平台架构分享.pptx

    微服务架构是一种现代的软件开发方法,它将大型应用程序分解为一组小型、独立的服务,每个服务专注于执行单一功能,从而实现高度的可伸缩性和可部署性。这种架构模式源于软件开发的历史演变,从早期的ASP、JSP、PHP...

    workshop专场微服务专场开发者动手实践营Fescar在微服务架构下分布式一致性的实践.pdf

    它将一个业务操作拆分为Try、Confirm和Cancel三个阶段。Try阶段用于预留资源并记录日志;Confirm阶段是在所有参与者都成功Try后执行的实际业务操作;Cancel阶段则是Try阶段的逆操作,用于释放预留的资源。这种模型...

    微服务落地的难点解析.docx

    微服务架构作为一种现代化的软件开发方式,它将大型单体应用拆分为一系列小而独立的服务,每个服务都可以独立开发、部署和扩展。然而,微服务的落地实施并非易事,本文将深入探讨微服务落地过程中遇到的主要难点,并...

    基于微服务的预付卡系统的研究与设计.docx

    微服务是一种架构风格,它提倡将单一应用程序开发成一组小型服务,每个服务运行在其独立的进程中并通过轻量级机制互相沟通。每个服务都是围绕业务能力构建的,并且能够独立部署。 ##### 2.2.2 微服务设计模式 ...

    企业中台技术架构解决方案.pdf

    - **从单体架构到微服务架构**:单体架构在系统规模增长时面临维护难题,而微服务架构通过服务解耦,使得每个服务能独立扩展。 - **全面使用分布式架构**:分布式计算成为云技术的核心,通过横向扩展提升性能,...

    seata-server-0.9.0.rar(亲测有效!)

    在 SpringCloud Alibaba 的生态中,Seata(前身是 Fescar)是一个重要的组件,它解决了微服务架构下分布式事务的难题。 分布式事务在微服务架构中是一个关键问题,因为传统的 ACID(原子性、一致性、隔离性、持久性...

    seata-server-1.3.0.zip

    Seata(Simple Extensible Autonomous Transaction Architecture)是一款备受瞩目的开源分布式事务框架,旨在...通过合理地使用Seata,开发人员可以有效地解决微服务架构下的事务管理难题,提升系统的可靠性和性能。

    seata-server-1.2.0.zip

    这种模式下,每个业务操作被分为尝试、确认和取消三个阶段。尝试阶段检查业务是否可执行,确认阶段执行业务,取消阶段则用于回滚业务。 5. **微服务集成**: Seata与常见的微服务框架如Spring Cloud和Dubbo有很好...

    三分钟彻底弄懂什么是分布式和微服务架构

    分布式和微服务架构是现代软件开发中的重要概念,它们源于对传统单一系统无法应对复杂性和扩展性的挑战。本文将深入探讨这两个概念以及相关的技术问题。 首先,微服务架构源自分而治之的设计理念,它是对大型系统...

    基于Spring Cloud搭建微服应用.pptx

    【Spring Cloud微服务构建】\n\n微服务架构已经成为现代IT行业中应对复杂业务场景的重要解决方案。它提倡将单一应用程序划分为一组小的服务,每个服务都在自己的进程中运行,服务之间通过轻量级的方式(通常是...

    分布式事务终极解决方案汇总.docx

    在现代软件开发尤其是微服务架构下,分布式事务成为了一项极为重要的技术议题。当一个应用程序由多个分散的服务构成时,确保这些服务间数据交互的一致性和完整性便成为了挑战。本篇文章旨在探讨分布式事务的相关概念...

    传统企业代码级中台落地实践解决方案zz.pptx

    - **技术架构**:从单体架构向服务化架构乃至微服务架构转变。 - **数据架构**:从简单的统计分析到支持管理决策的数据运营驱动。 - **研发流程**:从手工化的测试发布到DevOps自动化。 - **组织架构**:建立中台...

    6种分布式事务最终一致性解决方案,一次性说清.docx

    4. **分布式事务状态机(TCC)**:Try、Confirm、Cancel三个操作构成,每个服务执行尝试操作,成功后再确认,失败则取消。TCC适合业务逻辑可以拆分为明确的尝试和确认操作的场景。 5. **事件驱动一致性(Eventual ...

    taotao商城综合项目dubbo版

    - **Saga**:一种长事务模式,通过一系列本地事务来完成一个全局事务的过程,适用于复杂业务流程。 ##### 5.3 系统监控与日志收集 为了更好地监控系统的运行状态并及时发现潜在问题,Taotao商城综合项目中还集成了...

    重复消费、顺序消费、分布式事务.pdf

    1. **跨服务调用**:在微服务架构下,一个业务流程可能涉及多个服务之间的调用,如何保证这些调用的一致性成为一大难题。 2. **网络延迟**:在网络不稳定的环境下,分布式事务的性能和稳定性也会受到影响。 3. **...

    JAVA-项目面试题.docx

    微服务是一种将大型应用拆分成小型、独立、无状态且高度解耦的服务架构。这种架构强调服务之间的松耦合、可独立部署和扩展,便于敏捷开发和分布式系统的构建。 16. **微服务与SOA架构比较** 微服务更注重服务的轻...

Global site tag (gtag.js) - Google Analytics