前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >在微服务之间进行通信

在微服务之间进行通信

作者头像
Steve Wang
发布2018-06-20 10:35:40
2.7K0
发布2018-06-20 10:35:40
举报
文章被收录于专栏:从流域到海域从流域到海域

Communicating Between Microservices

原文作者:Piotr Mińkowski

原文地址:https://dzone.com/articles/communicating-between-microservices

译者微博:@从流域到海域

译者博客:blog.csdn.net/solo95

(monolithic application, 下面全部译为整体应用程序,即是一个包含多个微服务的完整的应用程序,因为是一个微服务的集合体,自然跟单个微服务相比体量庞大,monolithic也有庞大的意思。)

微服务间的通信

开发微服务而不是整体的应用程序(monolithic application)最重要的方面之一是跨服务通信。在整体的应用程序中,运行于组件之间的单个进程调用是使用语言层面上的方法调用上实现的。如果在开发过程中遵循了MVC设计模式,通常会有将关系数据库映射到对象模型的模型类。然后,您会创建一些组件,这些组件暴露出一些方法以帮助在数据库的表上执行标准操作(如创建、读取、更新和删除)的方法。大家所熟知的DAO或存储库对象的组件不应该直接从控制器那里调用,而是通过一个附加组件层(来调用),如果有需要的话,也可以在组件上添加一些业务逻辑。

通常,当我和其他人讨论如何把一个整体的程序迁移到一个基于微服务的应用程序时,他们认为的最大挑战仅仅是改变他们的通信机制。如果您回想起一个典型的有数据库后端的整体应用程序的相关工作,您可能就会意识到如何正确地设计表之间的关系,然后将它们映射到对象模型中是多么的重要。在基于微服务的体系结构中,重要的是将这个通常非常复杂的结构划分成能够独立开发和部署的服务,这些服务也将形成具有多个通信链路的网络。通常,划分并不像它看起来的那么明显,并不是每一个包含了表相关逻辑的组件都要变成分离的微服务。

(做出)与这种划分相关的决策需要了解系统的业务方面,但是通信标准却可以容易地定义,而且无论我们决定实施哪种(通信)方法,它们都是不可改变的。如果我们讨论的是通信风格,有可能把它们分为两个核心。第一步要做的是去定义协议是同步的还是异步的。

  • 同步——对于Web应用程序的通信来说,HTTP协议已经成为多年来的标准,对于微服务来说也没有什么不同。它(HTTP协议)是一个同步的、无状态的协议,它确实有它的缺点。然而,这些去欸但并没有对其受欢迎程度产生负面影响。在同步通信中,客户端发送请求并等待来自服务的响应。有趣的是,使用该协议,客户端却可以与服务器进行异步通信,这意味着线程不会被阻塞,并且响应最终会抵达回调(函数)。这种提供同步REST通信这种最常见模式的一个例子是Spring Cloud Netflix。对于异步回调,我们有像Vert.x或NoDE.js平台这样的框架。
  • 异步——这里的关键点是客户端在等待响应时不应该阻塞线程。在大多数情况下,这种通信是通过消息代理实现的。消息生成器通常不等待响应(回复)。它只是等待确认消息已经被消息代理所接收。这种类型的通信最流行的协议是AMQP(高级消息队列协议),它被许多操作系统和云提供商所支持。异步消息传递系统可以在一对一(队列(Queue))或一对多(主题(Topic))模式中实现。最受欢迎的消息代理是RabBMQ和Apache Kafka。Spring Cloud Stream是一个有趣的框架,它提供了基于这些代理建立来消息驱动式微服务的机制。

大多数人认为,构建微服务是基于和使用JSON Web服务的REST相同的原则。当然,这是最常见的方法,但正如你所看到的,它不是唯一的方法。不仅如此,在某些文章中,您可能会看到同步通信是一种反模式,尤其是当呼叫调用路径中有许多服务时。

我们可以参考的另一个频繁进行的对比是将微服务与SOA架构进行了比较。在SOA,最常见的通信协议是SOAP。关于SOAP是否比REST好,或者相反,已经进行过大量的讨论。众所周知,它们都有优点和缺点,但REST是轻量级且独立于语言的种类,因此它赢得了现代应用程序的竞争,并且正在慢慢接管企业部门。老实说,如果有一个很好的理由,我不会反对任何基于SOAP的微服务。

让我们回顾一下不同类型通信的划分标准。我已经提到,我们可以将它们分类为同步与异步,后者定义了通信具有单个接收器还是多个接收器。在一对一通信中,每个客户端请求都由一个服务实例来处理,而每个请求可以由许多不同的服务处理。这里值得指出的是,一个消息是由不同的服务接收的,但通常不应该由单个服务的不同实例接收。微服务框架通常会实现消费者的分组机制,由此单个应用的不同实例会被放置在竞争的消费者关系中,而其中只有一个实例应该去处理传入的消息。

对于一对一同步服务而言,可以在客户端执行负载平衡机制来实现相同的服务。每个服务都有关于调用该服务的所有实例的位置地址信息。该信息可以从服务发现服务器(service discovery server)中获取,或者可以手动配置其属性来提供。每个服务都有一个内置的路由客户端,可以使用正确的算法来选择目标服务的一个实例,并在实例上发送请求。下面这些是最普遍的负载平衡方法:

Round Robin(轮询调度)-最简单和最常见的方式。请求顺序地分布在所有实例中。

Least Connections(最小连接)-请求转到当前正在处理最少数量的活动连接的实例。

Weighted Round Robin(加权轮询调度)-该算法为池中的每个实例分配权重,并且新的连接依照分配的权重按比例转发。

IP Hash(IP 哈希)-此方法从源IP地址生成唯一的哈希密钥,并确定哪个实例接收请求。

下面有一幅图,它描绘了基于微服务架构的不同类型的通信,假定每个服务有多个实例存在:

在更复杂的体系结构中,可以存在三种通信类型相互混合的情况。然后,一些微服务是建立在同步交互的基础上,另一些是基于一对一消息传递,其余是基于发布/订阅模型的。

最近有很多关于响应式微服务的讨论,所以我认为这值得一提。它基于响应式编程范式(Reactive Programming paradigm),面向数据流和更改的传播。这样的微服务是非阻塞的、异步的、事件驱动的,并且需要少量的线程来扩展。它们最大的优点是性能优良,资源消耗小。建立响应式微服务最流行的框架是Lagom和Vert.x。

让我们回到同步的请求/响应通信。在部分失败的情况下准备系统非常重要,尤其是对于基于微服务的体系结构,其中有许多应用程序在各自独立的进程中运行。来自客户角度的单个请求可能会通过许多不同的服务转发。有可能是因为失败,维护或仅仅可能是超载而导致其中一项服务中断,这会导致对进入系统的客户端请求的响应速度变得非常慢。我们已经有处理故障和错误的几个最佳实践。第一种方法建议我们应该始终设置网络连接超时和读取超时,以避免等待响应时间太长。第二种方法是在服务失败或响应时间过长的情况下限制接受请求的数量。

后两种模式彼此紧密相连。我正在考虑断路器模式和回退。这种方法的主要假设依赖于监测成功和失败请求的监测。如果有太多的请求失败或者服务需要耗费很长时间才能响应,那么配置的断路器就会被触发,并且所有进一步的请求都会被拒绝。另一方面,回退提供了一些(紧急)逻辑部分,如果请求失败或断路器跳闸,则该逻辑必须被执行。在某些情况下,它可能很有用,尤其是当服务返回的数据对客户端不重要或者不会频繁进行更改并且可能从直接缓存中获取时。Netflix Hystrix提供了上面所描述模式的最普遍的实现,许多Java框架都使用它,

使用Spring Cloud Netflix实现断路器非常简单。在主类中,可以使用一个注释来启用它:

代码语言:txt
复制
@SpringBootApplication
@EnableFeignClients
@EnableCircuitBreaker
public class Application {
 public static void main(String[] args) {
  SpringApplication.run(Application.class, args);
 }
}

为了与其他微服务进行通信,我们可以使用Feign REST客户端来处理回退。在这里,我们返回一个空的列表:

代码语言:txt
复制
@FeignClient(value = “account - service”, fallback =
 AccountFallback.class)
public interface AccountClient {
 @RequestMapping(method = RequestMethod.GET, value = “/accounts/customer / {
  customerId
 }”)
 List < Account > getAccounts(@PathVariable(“customerId”) Integer customerId);
}
@Component
public class AccountFallback implements AccountClient {
 @Override
 public List < Account > getAccounts(Integer customerId) {
  List < Account > acc = new ArrayList < Account > ();
  return acc;
 }
}

Hystrix的默认设置可能会被配置属性覆盖。下面的可见属性设置了在等待响应时,调用者将收到超时信息:

代码语言:txt
复制
hystrix.command.default.execution.isolation.thread.
timeoutInMilliseconds=500

这篇文章在新的DZone微服务指南中有介绍。获取更具洞察力文章的免费副本,行业统计数据,以及更多!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Communicating Between Microservices
  • 微服务间的通信
相关产品与服务
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档