本文翻译自 How To Implement Synchronous Interactions Between Microservices (opens new window),原作者:OLEKSII
在之前的文章《如何理解微服务架构》中,我们回顾了微服务架构的优缺点。在采用这种架构构建的应用程序中,服务之间的交互可能是一个挑战。实现这种交互有不同的方法。在本篇文章中,我们将回顾同步交互(HTTP/HTTPS)。
# 同步微服务交互
让我们回顾一下上面的示例。在本例中,我们采用的是每个服务一个数据库
模式。这意味着服务不共享数据库。每个服务都有自己的数据库或根本没有数据库。
# 同步微服务交互示例
用户访问酒店预订网站并选择入住日期。Hotel Service(酒店服务)
只返回一个包含尽可能少的酒店客房信息的列表。然后,用户选择一个特定的酒店客房。因此,我们需要输入酒店客房和入住日期。预计将返回完整的酒店信息:
- 酒店摘要。例如,名称、地址、评级、描述、可用服务、房间描述等。
Hotel Service(酒店服务)
使用自己的数据库获取所需数据。 - 酒店附近的景点。可以是餐厅、剧院、电影院、博物馆等。这些信息通常不会更改。因此,这些信息可以保存在
Hotel Attractions Service(酒店景点服务)
数据库中。 - 酒店附近的活动。这些是不时发生的事件。
第三方事件服务
可用于获取入住期间酒店附近的活动(如节日、音乐会、展览等)。
总之,响应包含来自三个不同服务的数据,它们通过 HTTP/HTTPS
进行同步通信。
# 同步微服务模式
乍一看,从Hotel Service(酒店服务)
向 Hotel Attractions Service(酒店景点服务)
以及从 Hotel Attractions Service(酒店景点服务)
向第三方事件服务
发出请求似乎就足够了。然而,这种微服务交互方法存在一些常见问题。
# 服务发现
第一个问题是 Hotel Service(酒店服务)
不能直接调用 Hotel Attractions Service(酒店景点服务)
(我们不能硬编码服务 URL)。为了提供稳定的服务,同一服务的多个实例应并行运行。实例的数量可根据负载情况增减,不健康的实例会被健康的实例取代。因此,一些基础设施组件应负责活动服务及其活动健康实例的列表。服务发现
就是用于此目的。有关运行服务实例的信息保存在服务注册中。
例如,在 Spring 体系中就是 Eureka。
# 客户端负载均衡
使用服务注册
的第一个选项是客户端负载均衡
。
这意味着什么?我们的服务注册
提供有关健康实例的信息。Hotel Service(酒店服务)*
可与服务注册
交互,以查找 Hotel Attractions Service(酒店景点服务)
的活动服务实例,并使用某种算法来选择要使用的实例(如:循环演算法)。
调用将被执行到选定的服务实例。
在 Spring 中 通常是 Ribbon。
# 网关
使用服务注册
的第二种方法是使用网关(如文章开始的图片所示)。
在这种情况下,一个特殊组件负责将请求重定向到所需的服务实例。网关与服务注册中心合作,了解哪些服务及其实例可以使用。
例如,它可以这样工作:
- 用户(个人或其他服务)调用,如:
GET https://hotelbookingapplication.com/api/hotel-attractions?lattitude=15.45656&longitude=-18.99908
等服务。 hotel-attractions
路径表明服务信息。- 网关查找有关
hotel-attractions
服务的信息。 - 找到正在运行的
hotel-attractions
服务实例的 URL。 - 例如,使用循环演算法选择一个实例并发出请求。
在 Spring 中通常是 Zuul。
# 断路器
让我们回顾一下同步微服务交互的另一个问题。
在我们的示例中,将执行下一个请求序列:
Hotel Service(酒店服务)
向酒店景点服务发出请求。Hotel Attractions Service(酒店景点服务)
向第三方事件服务
发出请求。
如您所见,该请求取决于三项服务。此外,第三方事件服务
不在我们的控制范围之内。
如果其中一个服务运行缓慢,会发生什么情况?它会对整个应用程序产生什么影响?
问题在于,即使一个服务出现问题,也会对整个系统的性能产生负面影响。
试想一下第三方事件服务
宕机时的情况。在这种情况下,用户甚至无法看到酒店客房信息。没错,第三方事件服务
提供的酒店附近活动信息对某些人来说可能很重要。但是,这并不重要。没有这些信息,人们仍然可以预订酒店房间。
如果第三方事件服务
性能不佳,情况就会更糟。
想象以下的情景:
- 向
Hotel Service(酒店服务)
提出申请。 - 从
Hotel Service(酒店服务)
向Hotel Attractions Service(酒店景点服务)
发出请求。 Hotel Attractions Service(酒店景点服务)
向第三方事件服务
发出请求。第三方事件服务
在 30 秒内响应。
因此:
Hotel Attractions Service(酒店景点服务)
阻塞线程,等待第三方事件服务
的响应。Hotel Service(酒店服务)
阻塞线程,等待Hotel Attractions Service(酒店景点服务)
的响应。
在单个请求的情况下,并不会发生什么严重的情况:用户只需等待大约 30 秒的响应。
然而,这里有两个主要问题:
- 不是每个用户都会等待 30 秒。
- 有关酒店附近活动的信息并不重要。
当我们看到真实情况时,情况会变得更糟:用户提出的请求不止一个。很多用户都在寻找酒店房间。
这意味着这 30 秒的等待时间会对整个系统的性能产生负面影响:
Hotel Attractions Service(酒店景点服务)
资源已耗尽。Hotel Service(酒店服务)
更加等待Hotel Attractions Service(酒店景点服务)
的响应。Hotel Service(酒店服务)
的资源耗尽。Hotel Service(酒店服务)
无法处理与既未使用Hotel Attractions Service(酒店景点服务)
也未使用第三方事件服务
的酒店房间列表相关的请求。
解决方案非常简单。使用断路器。
- 为每个请求设置超时。
- 如果一个服务在超时前未收到另一个服务的响应,则初始服务将以默认值响应。
- 在某个配置的时间段内,不会向有问题的服务发出请求。始终使用默认响应。
- 一段时间后,再次向相关服务发出请求。
让我们回顾一下第三方活动服务出现问题时的情况(同样适用于酒店景点服务)。
Hotel Service(酒店服务)
向Hotel Attractions Service(酒店景点服务)
发出请求。Hotel Service(酒店服务)
向第三方活动服务发出请求,等待响应 1 秒钟。- 如果收到成功响应,则一切正常。用户收到所有数据。
- 如果响应不成功,用户将不会收到有关事件的信息。后续请求(例如下一个 5 秒)将跳过对有问题的
第三方事件服务
的调用。
在 Spring 中,典型的是 Hystrix。
# 缓存
为了优化性能特征,在某些情况下可以使用缓存。
如果可能,可以出于以下原因使用缓存:
- 跳过对目标服务的调用。
- 在目标服务出现问题时,可以向用户返回一些默认数据(可能是过时或不准确的数据)。
不过,缓存失效可能是一个真正的挑战。
# 总结
本文介绍了同步微服务交互面临的各种挑战。这种方法在某些情况下是适用的。不过,还有另一种微服务通信方式。下一篇文章将讨论异步微服务交互。