容错(fault tolerance)

分布式系统世界中,随时随地都可能会出现异常。Micro在定位这个问题时,对容错作出了许多最好的尝试。这篇文档介绍一些可配置的容错方式。

心跳(Heartbeating)

心跳是服务发现中,刷新服务的注册状态的机制。

原理

服务通过服务发现功能,在启动时进行服务注册,关闭时进行服务卸载。有时候这些服务可能会异常挂掉,进程可能会被杀掉,可能遇到短暂的网络问题。这种情况下,节点会在服务发现中被干掉。理想状态是服务会被自动移除。

解决方案

为了解决这个问题,Micro注册机制支持通过TTL(Time-To-Live)和间隔时间注册两种方式。TTL指定一次注册在注册中心的有效期,过期后便删除,而间隔时间注册则是定时向注册中心重新注册以保证服务仍在线。

这两个方式在go-micro中都可以选项或micro命令flag的方式指定。

使用方法

如果是命令行的方式,则可以方便使用内置的flag设置ttl值、间隔时间

  1. micro --register_ttl=30 --register_interval=15 api

刚这个例子我们设置了30秒的TTL生存期,并设置了每15秒一次的重注册。

而对于使用go-micro机制,可以以在构造服务时把选项传进去,比如time.Duration*n

  1. service := micro.NewService(
  2. micro.Name("com.example.srv.foo"),
  3. micro.RegisterTTL(time.Second*30),
  4. micro.RegisterInterval(time.Second*15),
  5. )

负载均衡

负载均衡是一种将请求摊开负载或者维持高可用的方式。

原理

对于一些单线程的应用来说,高可用和弹性能力非常有限。当应用因某种原因宕掉这宾便不能再提供服务。当有足够多的请求负载到这台服务上时,那它可能会响应得很慢或者不会响应全部请求。而把所有请求分发来多台应用的副本能很好解决这个问题。

解决方案

微服务客户端通过选择器负载均衡,选择器可以把请求分到任意多的服务节点上。服务启动后,它使用唯一的地址与id组合向注册中心注册成服务节点。创建请求时,micro客户端会通过选择器决定向哪一个节点发送请求。选择器在服务注册信息中找到服务的节点,然后用负载均衡策略选择一个节点把请求发送出去,比如随机哈希、轮询。

使用方式

客户端负载均衡内置在go-micro客户端中,这是自动完成的。

重试

重试是指请求不成功时再次请求的方法。

原理

请求可能会因为某种原因失败,网络、负载、宕机等等,理想情况是,在发生这些事后,我们仍能能把请求重新发到程序的另一个节点并收到成功的响应。

解决方案

micro客户端内置有重试机制。选择器(上面有提到)会返回一个叫Next的函数,这个函数在执行时会基于负载均衡策略从服务列表中返回一个节点。Next函数可以执行多次,基于负载均衡策略返回新的节点。如果重试有设置,在请求失败时,Next函数便会执行并把请求发向另一个新的节点。

使用方式

重置可以通过命令行标记或代码选项在客户端传入。默认值是1,也即是一次请求尝试一回。

通过命令行flag传入

  1. micro --client_retries=3

设置选项

  1. client.Init(
  2. client.Retries(3),
  3. )

服务发现缓存

服务发现缓存是指在客户端缓存服务发现的信息。

原理

服务发现是微服务的核心,但是如果架构设计不对,它也会变成故障节点。每种发现系统都有它自己的弹性与高可用属性。当发生发现机制无法使用的事故,这会导致剩下的系统都不可用,那么服务也都无法判断如何解析服务名与地址。而如果每次请求都要到中心服务节点查找对于服务发现也是个瓶颈。

解决方案

客户端进行缓存可以消除服务发现作为中心的性能瓶颈及故障节点失效的问题。Micro包含有选择器(客户端负载均衡器),它在缓存中维护了服务发现的信息。如果缓存没有找到,选择器会到注册服务中心去查找并把结果缓存起来。缓存也会设置TTL定期调整,以确保过期的数据不再生效。

使用方式

selector缓存可以使用命令行参数或创建服务时指定

命令行的方式

  1. micro --selector=cache api

如果要调用Init方法,Go-micro服务也支持使用同样的命令行flag来传递该参数:

  1. import (
  2. "github.com/micro/go-micro/client"
  3. "github.com/micro/go-micro/selector/cache"
  4. )
  5. service := micro.NewService(
  6. micro.Name("com.example.srv.foo"),
  7. )
  8. service.Client().Init(cache.NewSelector())