微服务通信的不同方面总结

2025-06-07

微服务通信的不同方面

综上所述

微服务是目前软件开发的“流行”方式,而且已经流行了一段时间了。它们似乎已经过了软件界盛行的“新奇玩意儿”的阶段(我指的是 JavaScript 框架)。

最近,我花了很多时间将单体应用迁移到微服务,或者从头构建新的微服务。最大的挑战之一始终是沟通。

在传统的单体应用中,应用程序的不同组件之间通信相对轻松便捷。这里调用一个方法,那里使用一个 using 语句。一切都简洁明了。

请看下面这个相当牵强的例子:


using MySampleApp.ProductModule;

namespace MySampleApp.OrderModule
{
    public class OrderService 
    {    
        private readonly IProductService _productService;

        public OrderService (IProductService productService)
        {
            this._productService = productService;
        }

        public void PlaceOrder(Order newOrder)
        {
            this._productService.ValidateProducts(newOrder.Products);

        }
    }
}

Enter fullscreen mode Exit fullscreen mode

下了一个新订单,订单中的产品需要验证。该应用有一个大型共享代码库。产品服务可以简单地注入到订单服务中并使用。很简单。

微服务的复杂性

想象一下同样的场景,但订单服务和产品服务是完全独立的应用程序。从真正意义上讲,微服务是完全独立的进程,彼此之间没有直接关系。

整体式结构听起来还那么糟糕吗?

那么,这两个截然不同的过程如何沟通呢?如果按照它们的设计,它们之间应该没有直接的联系。幸运的是,有很多选择。

好好休息

REST 已经存在很长时间了。它是一种通过 HTTP 请求进行通信的简单协议。

请求本身有一些标准(GET,PUT,POST等),但请求的内容由服务本身决定。

优点

REST 是一种文档齐全、标准化且极其常见的通信方式。我相信几乎所有阅读本文的人,在其开发生涯的某个阶段,都曾与 RESTful API 进行过交互。

几乎每种语言都有内置支持,并且有数百个第三方库为标准 HTTP 调用添加了更多功能。

它也相当解耦。只需要一个 API 的 URL 就可以开始使用了。不过我个人认为,有时它解耦得有点过头了,反而不利于它本身。这很好地引出了

REST 响应的内容完全可以由 API 本身更改。

假设订单团队希望 API 响应中包含一个名为“productCode”的属性。产品团队正在进行一次大型重构/清理会议,并将该属性重命名为“code”。这完全正常,但订单服务却突然停止运行。

根据经验,这些问题总是很难发现,并且根据使用情况,可能会在很长一段时间内被忽视。

还有端点本身的问题。在一个大型应用程序中,可能有数百个这样的微服务。如果所有微服务都通过 HTTP 进行通信,那么需要跟踪的设置/端点就非常多。

API 网关可以用来提供一个标准的基础端点,所有请求都会通过该端点进行路由。这确实会增加额外的复杂性和单点故障,但可以抵消数百个不同端点带来的一些复杂性。

事件总线

事件总线(或消息代理)是一种用于协调应用程序之间通信的模式。它们最大限度地减少了不同服务之间的相互感知,是迄今为止管理服务间通信最解耦的方式。

在我们的示例场景中,订单服务会向事件总线发起“ValidateProducts”事件。该事件可以是同步的(请求/响应),也可以是异步的(发布/订阅)。

产品服务将连接到 EventBus 并监听所有符合一组条件的事件。在本例中,所有符合条件的事件均为 ValidateProducts 事件。收到事件后,系统会对其进行处理并将其标记为完成。

如果需要响应,事件处理程序会将响应的内容返回给事件总线,然后事件总线管理返回原始请求者的路由。

事件总线的世界神奇地解耦了!

事件总线提供商有很多,本文我不会一一详述。但有一些需要考虑的事项

优点

在我看来,事件总线提供了最佳的解耦效果。所有 10/100/1000 个服务都通过一个公共媒介进行通信,彼此之间完全互不了解。

它还提供了一种直观的代码结构。订单服务创建订单,然后发布“NewOrderAdded”事件。它不需要关心谁/什么感兴趣,它只关心创建订单并让世界知道它。

缺点与 REST 非常相似。虽然解耦程度更高,但结构变化的问题仍然存在。两端的事件预计具有相同的对象模型。如果其中一方发生改变,同样的问题也会出现。

就常见的 .NET 实现而言,事件会被序列化为 JSON 并传递。如果 JSON 属性名称发生变化,则该属性将不会被解析。

所有事件都通过事件总线处理,也容易导致单点故障。虽然大多数提供商支持集群和故障转移,但仍然有一个可能出错的地方。

gRPC

gRPC 是一个相对较新的技术,在我在这里介绍的 3 个选项中,它是我最不熟悉的一个。

它是一个开源的远程过程调用 (RPC) 系统,最初由 Google 构建。传输使用 HTTP/2,并支持标准的协议接口。对于所有 .NET 开发者来说,可以考虑使用与语言无关的 WCF。

gRPC 服务会生成一个 .proto 文件。这些文件可供服务使用,并充当调用者和远程服务器之间的接口描述。proto 文件涵盖方法签名、变量和响应类型。所有内容都包含在一个文件中。

现在几乎所有流行的编程语言都支持 gRPC 实现。

优点

我最喜欢 gRPC 的一点是其严格的接口。要调用 gRPC 服务上的方法,必须使用相应的 .proto 文件,因此方法的调用方式是明确的。这样就不会出现属性名称更改或序列化问题。

它也快如闪电。服务之间直接通信,数据被紧密打包并使用 HTTP/2 发送,HTTP/2 具有许多基于压缩的优势。

时间。这可能更多是出于个人原因,而非协议本身的考虑。但如果我用 .NET Core 编写两个服务,一个使用 REST,另一个使用 gRPC,那么 gRPC 服务的集成会更加繁琐。

作为 .NET 开发者,gRPC 服务必须强制以 HTTPS 协议运行,这会让部署变得困难。尤其对于在客户端现有基础设施内运行且无需任何外部访问的服务而言。

与 REST 类似,它也具有每个服务明确指定端点的特性。因此,订单服务要向产品服务发出请求,必须知道产品服务的确切位置。这种模式易于管理,但规模化可能比较棘手。

综上所述

我在探索微服务世界的过程中使用了三种不同的方法。

每种方法都有其优缺点,而这三者之间存在着足够的差异,足以抵消几乎所有的缺点。我相信,这才是解决跨服务通信之争的答案。根据具体情况,将三者结合起来。

对于已经发生的异步操作(NewOrderCreated),使用事件总线;为了速度,使用 gRPC;为了简单,使用 REST。

本文所有观点均来自我个人的软件开发经验,但我一直渴望学习更多。如果我遗漏了什么,或者您想补充什么,请在下方留言。我喜欢积极的讨论!

文章来源:https://dev.to/jeastham1993/the- Different-faces-of-microservice-communication-22i9
PREV
TailwindCSS 初学者指南!
NEXT
基于属性的测试简介 生成测试的目的是什么?它会取代单元测试吗?它有哪些常见的属性?PBT 在“现实世界”中真的有用吗?PBT 套件中有哪些值得关注的优秀部分?我在哪里可以了解更多信息?