微服务之间的调用方法哪种更好?
发布时间:2023-02-15 11:24:39 所属栏目:云计算 来源:互联网
导读:在微服务架构中,需要调用很多服务才能完成一项功能。服务之间如何互相调用就变成微服务架构中的一个关键问题。 消息方式是松耦合方式,比紧耦合的RPC方式要优越,但RPC方式如果用在适合的场景也有它的一席之地。 我们总在谈耦合,那么耦合到底意味着什么呢
在微服务架构中,需要调用很多服务才能完成一项功能。服务之间如何互相调用就变成微服务架构中的一个关键问题。 消息方式是松耦合方式,比紧耦合的RPC方式要优越,但RPC方式如果用在适合的场景也有它的一席之地。 我们总在谈耦合,那么耦合到底意味着什么呢? 一、耦合的种类 1)时间耦合:客户端和服务端必须同时上线才能工作。发消息时,接受消息队列必须运行,但后台处理程序暂时不工作也不影响。 2)容量耦合:客户端和服务端的处理容量必须匹配。发消息时,如果后台处理能力不足也不要紧,消息队列会起到缓冲的作用。 3)发送方式耦合:RPC是点对点方式,需要知道对方是谁,它的好处是能够传回返回值。消息既可以点对点,也可以用广播的方式,这样减少了耦合,但也使返回值比较困难。 二、事件驱动(Event-Driven)方式 Martin Fowler把事件驱动分成四种方式(What do you mean by “Event-Driven”),简化之后本质上只有两种方式。一种就是我们熟悉的的事件通知(Event Notification),另一种是事件溯源(Event Sourcing)。 事件通知就是微服务之间不直接调用,而是通过发消息来进行合作。事件溯源有点像记账,它把所有的事件都记录下来,作为永久存储层,再在它的基础之上构建应用程序。 实际上从应用的角度来讲,它们并不应该分属一类,它们的用途完全不同。事件通知是微服务的调用(或集成)方式,应该和RPC分在一起。事件溯源是一种存储数据的方式,应该和数据库分在一起。 1、事件通知(Event Notification) 先说读数据,假设要创建一个“Order”,在这个过程中需要读取“Customer”的数据和“Product”数据。 如果用事件通知的方式就只能在“Order Service”本地也创建只读“Customer”和“Product”表,并把数据用消息的方式同步过来。 再说写数据,如果在创建一个“Order”时需要创建一个新的“Customer”或要修改“Customer”的信息,那么可以在界面上跳转到用户创建页面,然后在“Customer Service”创建用户之后再发”用户已创建“的消息,“Order Service”接到消息,更新本地“Customer”表。 这并不是一个很好的使用事件驱动的例子,因为事件驱动的优点就是不同的程序之间可以独立运行,没有绑定关系。但现在“Order Service”需要等待“Customer Service”创建完了之后才能继续运行,来完成整个创建“Order”的工作。主要是因为“Order”和“Customer”本身从逻辑上来讲就是紧耦合关系,没有“Customer”你是不能创建“Order”的。 在这种紧耦合的情况下,也可以使用RPC。你可以建立一个更高层级的管理程序来管理这些微服务之间的调用,这样“Order Service”就不必直接调用“Customer Service”了。 当然它从本质上来讲并没有解除耦合,只是把耦合转移到了上一层,但至少现在“order Service”和“Customer Service”可以互不影响了。之所以不能根除这种紧耦合关系是因为它们在业务上是紧耦合的。 对这个例子来讲,使用事件驱动是一个不错的选择,因为每个服务发消息之后它不需要任何反馈,这个消息由下一个模块接收来完成下一步动作,时间上的要求也比上一个要宽松。用事件驱动的好处是降低了耦合度,坏处是你现在不能在程序里找到整个购物过程的步骤。 如果一个业务逻辑有它自己相对固定的流程和步骤,那么使用RPC或业务流程管理(BPM)能够更方便地管理这些流程。在这种情况下选哪种方案呢?在我看来好处和坏处是大致相当的。从技术上来讲要选事件驱动,从业务上来讲要选RPC。不过现在越来越多的人采用事件通知作为微服务的集成方式,它似乎已经成了微服务之间的标椎调用方式。 2、事件溯源(Event Sourcing) 这是一种具有颠覆性质的的设计,它把系统中所有的数据都以事件(Event)的方式记录下来,它的持久存储叫Event Store, 一般是建立在数据库或消息队列(例如Kafka)基础之上,并提供了对事件进行操作的接口,例如事件的读写和查询。事件溯源是由领域驱动设计(Domain-Driven Design)提出来的。 DDD中有一个很重要的概念,有界上下文(Bounded Context),可以用有界上下文来划分微服务,每个有界上下文都可以是一个微服务。下面是有界上下文的示例。下图中有两个服务“Sales”和“Support”。 有界上下文的一个关键是如何处理共享成员, 在图中是“Customer”和“Product”。在不同的有界上下文中,共享成员的含义、用法以及他们的对象属性都会有些不同,DDD建议这些共享成员在各自的有界上下文中都分别建自己的类(包括数据库表),而不是共享。可以通过数据同步的手段来保持数据的一致性。下面还会详细讲解。 事件溯源的一个短板是数据查询,它有两种方式来解决。第一种是直接对stream进行查询,这只适合stream比较小并且查询比较简单的情况。 查询复杂的话,就要采用第二种方式,那就是建立一个只读数据库,把需要的数据放在库中进行查询。数据库中的数据通过监听Event Store中相关的事件来更新。 数据库存储方式只能保存当前状态,而事件溯源则存储了所有的历史状态,因而能根据需要回放到历史上任何一点的状态,具有很大优势。但它也不是一点问题都没有。 第一,它的程序比较复杂,因为事件是一等公民,你必须把业务逻辑按照事件的方式整理出来,然后用事件来驱动程序。第二,如果你要想修改事件或事件的格式就比较麻烦,因为旧的事件已经存储在Event Store里了(事件就像日志,是只读的),没有办法再改。 由于事件溯源和事件通知表面上看起来很像,不少人都搞不清楚它们的区别。事件通知只是微服务的集成方式,程序内部是不使用事件溯源的,内部实现仍然是传统的数据库方式。 只有当要与其他微服务集成时才会发消息。而在事件溯源中,事件是一等公民,可以不要数据库,全部数据都是按照事件的方式存储的。 虽然事件溯源的践行者有不同的意见,但有不少人都认为事件溯源不是微服务的集成方式,而是微服务的一种内部实现方式。因此,在一个系统中,可以某些微服务用事件溯源,另外一些微服务用数据库。 当你要集成这些微服务时,你可以用事件通知的方式。注意现在有两种不同的事件需要区分开,一种是微服务的内部事件,是颗粒度比较细的,这种事件只发送到这个微服务的stream中,只被事件溯源使用。 另一种是其他微服务也关心的,是颗粒度比较粗的,这种事件会放到另外一个或几个stream中,被多个微服务使用,是用来做服务之间集成的。这样做的好处是限制了事件的作用范围,减少了不相关事件对程序的干扰。详见"Domain Events vs. Event Sourcing"。 事件溯源出现已经很长时间了,虽然热度一直在上升(尤其是这两年),但总的来说非常缓慢,谈论的人不少,但生产环境使用的不多。究其原因就是应为它对现在的体系结构颠覆太大,需要更改数据存储结构和程序的工作方式,还是有一定风险的。 但如果用Kafka实现事件通知就一点问题都没有。总的来说,对大多数公司来讲事件溯源是有一定挑战的,应用时需要找到合适的场景。如果你要尝试的话,可以先拿一个微服务试水。 虽然现在事件驱动还有些生涩,但从长远来讲,还是很看好它的。像其他全新的技术一样,事件溯源需要大规模的适用场景来推动。例如容器技术就是因为微服务的流行和推动,才走向主流。 事件溯源以前的适用场景只限于记账和源代码库,局限性较大。区块链可能会成为它的下一个机遇,因为它用的也是事件溯源技术。 另外AI今后会渗入到具体程序中,使程序具有学习功能。而RPC模式注定没有自适应功能。事件驱动本身就具有对事件进行反应的能力,这是自我学习的基础。因此,这项技术长远来讲定会大放异彩,但短期内(3-5年)大概不会成为主流。 三、RPC方式 RPC的方式就是远程函数调用,像RESTFul,gRPC, DUBBO 都是这种方式。它一般是同步的,可以马上得到结果。在实际中,大多数应用都要求立刻得到结果,这时同步方式更有优势,代码也更简单。 熟悉微服务的人可能都知道服务网关(API Gateway)。当UI需要调用很多微服务时,它需要了解每个服务的接口,这个工作量很大。 于是就用服务网关创建了一个Facade,把几个微服务封装起来,这样UI就只调用服务网关就可以了,不需要去对付每一个微服务。下面是API Gateway示例图: 四、降低紧耦合的影响 什么是紧耦合的主要问题呢?就是客户端和服务端的升级不同步。服务端总是先升级,客户端可能有很多,如果要求它们同时升级是不现实的。它们有各自的部署时间表,一般都会选择在下一次部署时顺带升级。 它的坏处是当以后新的客户来了,看到两个差不多的函数就糊涂了,不知道该用那个。而且时间越长越严重,你的服务端可能功能增加的不多,但相似的函数却越来越多,无法选择。 (编辑:甘南站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |