【微服务架构】4 微服务间通信

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/gdp12315/article/details/89645247

4 微服务间通信

在分布式系统中,服务内部的通信很重要的。组成应用的微服务需要一起无缝的工作,向客户端提供服务。不同服务之间的通信有两种形式,同步和异步调用。

4.1 同步和异步

同步通信,即请求后立马获得响应。异步通信,是一种消息,不需要响应。

高度分布式系统中使用异步事件或消息就是强有力的应用场景。

对于任何一种调用风格,开放的API需要提供文档说明。事件或消息负载也需要提供文档说明。事件订阅者或API消费者应该容忍不识别的字段,它们可能是新的。服务应该抛出异常和处理错误数据。

同步信息(REST)

在分布式系统中,异步形式的消息是很有价值的。当应用清晰的请求和响应语义,或一个服务需要与另一个服务紧密关联行为,应该使用异步API。

很多RESTful 操作能解析JSON格式的数据,当让其他协议或格式的数据也可以。基于Java的微服务最好使用JSON数据格式。

异步信息(Event)

异步消息能够解耦服务。一个异步事件能够使用,如果事件创造者不需要响应信息。

一个来自外部客户端的请求在获取响应信息前可能会穿过多个微服务。如果每个调用都是同步的,那么整个时间会阻断其他请求。微服务系统越复杂,那么微服务之间的交互需要额外的请求,将导致系统延时更长。如果请求的进程可以被异步事件替代,那么可以用事件来取代。

使用响应式的事件,一个服务应该发布自己状态或活动的事件。其他服务可以订阅这些事件并协同响应。

事件是很好的方式组建新的交互模式,而不需要引入依赖。这种使得框架具有扩展性,不需要再服务创建的时候考虑,后续可以扩展。

微服务风格的架构需要每个微服务拥有自己的数据。这意味着当请求进来时,可能有好几个服务需要更新自己的数据库。服务应该使用事件来通知数据变化,其他模块可以订阅这些事件。

为了协调应用中的消息,我们可以使用任何一个可用的消息代理方案。例如AMQP、RabbitMQ、Apache Kafka, MQTT等。

4.2 容错

我们选用微服务架构,一个很重要的动机是微服务架构可以创建容错和弹性伸缩的应用。现代应用被要求具有接近零的停机时间,秒级以内的响应。微服务架构中每个独立的服务必须能持续工作,即便其他的服务已经宕机。这就要就微服务应用在设计时具有容错的能力。

弹性可变

当使用同步请求调用其他微服务时,我们使用的是具体API,API被事先定义了输入属性,以及输出属性。微服务环境中,这种API的请求和响应数据一般采用JSON格式。现实中,我们很难保证输入和输出的属性不发生更改。基本是最佳的应用设计实践,需求也是经常改变的,这会造成属性的增加、删除或改变。为了弹性的应对这些变化,微服务的设计者需要考虑到API的消费和生成。

APIs 消费
作为API的消费者,我们需要校验调用API后获取的响应数据,判断它是否包含我们功能执行所需的信息。如果获取JSON格式的数据,我们需要解析JSON数据,在执行其他转换之前。当我们校验JSON或解析JSON时,我们必须做两件事情:

1.只校验请求中需要使用的变量或熟悉
不需要校验请求提供的所有变量。如果我们使用这些请求中的参数,我们不需要它们。
2.接受未知的属性
不需要提交异常,如果我们接收到异常的变量。如果响应体共包含我们所需的信息,那么不用在意其他额外的属性。

选择JSON解析工具允许我们配置如何解析进来的数据。例如Jackson提供@JsonInclude和@JsonIgnoreProperties注解的方式解析数据。

遵从这两天规则,我们的微服务可以变得更加弹性来应对任何改变,而不会受到直接影响。基本微服务添加或移除我们并未使用的属性,微服务仍然正常工作。

APIs 生成

当提供外部客户端调用的API时,请求和响应中有两件事需要考虑:
1.接收未知的请求属性
如果一个服务调用API,请求中包括不需要的参数,那么服务可以丢弃这些参数值。只有引起不需要的失败时才返回错误信息。
2.只返回API调用相关的属性
留尽可能多的空间来实现服务后续的改变。避免分享的过程中泄露实现细节。

遵从这两条可以让服务更加稳健。

超时(Timeouts)

当请求其他微服务时,无论同步请求或异步请求,请求必须设置超时参数。因为,我们希望用完服务就走,而不应该等待不确定的响应。

异步请求可以设置超时参数,例如 Failsafe和 Netflix Hystrix内置了超时设置。

设置超时可以提升微服务的弹性,但是仍然会导致不好的用户体验,因为端到端的调用会变慢。如果可以的话将超时设置一致,那么服务A向服务B请求超时,同时服务B等待服务C。

熔断(Circuit Breakers)

使用超时保护请求,避免等待不确定的响应。然而,如果某个请求经常发生超时,这会浪费很多时间等待超时发生。

熔断(Circuit Breaker)是一种用来避免重复超时发生的技术。这种技术类似电路里面的保险丝。熔断会记录每个调用失败或超时的请求。如果这些请求次数总和达到了一定的阈值,它会阻断后续请求的调用,直接返回错误信息。它也提供一种机制来重新尝试请求,尝试几次或根据响应某个事件。

熔断对于调用外界服务至关重要,一般来说,使用现在的熔断库即可实现此功能。

隔板(Bulkheads)

在轮船中,隔板用来隔离船舱避免因一个船舱漏水造成整个船沉掉。微服务中的隔板(Bulkheads)类似轮船中的隔板概念。我们需要避免微服务中某个部分的失败不会对整个微服务系统造成影响。隔板模式就是关于如何创建微服务而不是使用某个特定工具或库。当我们创建微服务时,我们总是告诫自己如何分离不同的模块,并且避免滚雪球式的崩溃。

实现隔板模式最简单的方式是提供回退(fallbacks)。添加fallback运行应用继续工作,当非重重要的服务宕机时。例如,一个在线的零售店系统,其中一个服务提供用户推荐信息,如果推荐服务宕机了,那么用户仍然可以查询商品和下单。一个有趣的回退链例子,当调用个性内容失败时,回退成调用更加一般的内容,这将回退成返回缓存内容,而不是返回错误。

另一种策略,为了避免慢的或勉强的远程资源造成整个系统宕机,可以使用受限可用的资源来应对这些超出的请求,最常见的方式使用队列(queue),以及 信号量(semaphore)

队列
队列有一个大小设置,当请求添加到已满的队列时,该请求会直接抛错。队列会生成有限数量的worker,每个定义了服务最大的线程数,可以被远程资源阻塞。

信号量(semaphore)
semaphore机制工作原理,是提供多个permits,向远端发起请求是需要一个permit,当请求完成后会释放permit。

队列和信号量最显著的区别是在资源用尽时的处理不同,使用Semaphore方法,超出的请求会直接被跳过,如果不能获取到permit; 而队列的方式,请求会等待(除非队列满了,请求会被丢弃)。

猜你喜欢

转载自blog.csdn.net/gdp12315/article/details/89645247