本篇文章主要讲nats,在micro工具集中,nats模块集成nats消息系统。

NATS主要工作聚焦在服务发现、微服务同步/异步通信。

什么是NATS?

NATS是一款开源云原生的消息系统,或者更简单地说是一款消息总线,NATS以前是由Derek Collison创建,他是Apcera的创始人。NATS起源于VMware,并在基于ruby的系统中展露头角。

而Go的版本很早之前就已经有了,并且在那些追求高度可伸缩和高性能消息系统中稳步发展。

如果想了解更多NATS,可以访问nats.io或者加入论坛

为什么使用NATS?

为什么不使用NATS?过去我们使用很多种消息总线,NATS显然是很特殊的。过去的这些年,消息好像就像企业的救世主一样,在系统中的每一部分每一件事都要使用它。消息产生很多未能达到预期效果的错误、自身特性膨胀、高昂的技术成本,使得引入它们产生的问题比解决的问题更多。

NATS,比较起来,它更专注于解决性能和可用性的问题,同时又要保证自身的轻量。NATS模式是“随时在线”,并且“发送便忽略”(即:关注于随时创建连接并发送消息,但是不关心消息是否到达)。 NATS很简单,因为它的专注、轻量使得使得它成为微服务的主要候选方案之一。我们也相信,在那些关注消息的服务之中,它也会成为通信传输的主要候选方案。

NATS支持:

  • 高性能高伸缩
  • 高可用
  • 极其轻量
  • 最多传输一次
    NATS不支持:

  • 持久化

  • 事务
  • 强化传输模式
  • 企业队列
    上面简要介绍了什么是及为什么是NATS。我们来讨论为什么它适合Micro。

NATS之上的Micro

Micro作为可插拔的微服务架构,它允许只改动小部分代码便能替换依赖的插件。每一个Go-Micro框架中的接口都提供了微服务的构建模块。

registry负责服务发现

transport负责同步的消息通信

broker负责异步的消息通信

…等等

为每个组件创建插件就你实现相关的接口一样简单。在未来提交的文章中,我们会花比较多的时间来详细说明如何来编写插件。如果你想签出像NATS或者其它系统中的插件,比如etcd、kafka、rabbitmq等等,可以到github.com/micro/go-plugins里翻阅。

Micro的NATS插件是go-micro插件集中的基础,它可以用于在需要NATS消息的服务中集成。通过提供各式各样的go-micro接口,我们有可集成方案供各种架构选择。

在我们以往的经验中,一个规格的插件不可能会适合所有的需求,灵活性也不完全会满足你们团队的模型。

下面我们讨论NATS插件的传输、代理、注册如何实现

传输

NATS - 图1

上面是go-micro的同步传输接口。它使用我们常见的Go中套接字语法,比如ListenDialAccept。这些概念及模式在tcp、http同步消息中很常见,但是使用在消息总线上时,就显得困难得多,与连接消息总线创建链接而不是服务本身。为了避开这个问题,我们使用带有主题、信道的伪连接的概念。

下面介绍它的工作过程

服用使用transport.Listen来侦听消息。这个过程会创建连向NATS的连接。当transport.Accept被调用后,会创建唯一的主题,并向这个主题订阅。该唯一的主题在go-micro中也会被当成服务地址。每个消息在收到后会立马被用作伪套接字或连接的基础。如果面向相同的回复地址的连接已经创建,那我们会把这个消息塞到该连接的待办表中。

客户端如果想服务端取得联系,则需要使用transport.Dial来创建面向服务端的连接。

该过程会与NATS连接,创建它自己的唯一主题并订阅该主题。该主题会用于服务端的响应。任何时候消息被客户端发送到服务端时,它会在主题设置该回复地址。

当任何一边想关掉连接时,会调用transport.Close来关闭与NATS的连接。

NATS - 图2

传输插件

导入传输插件

  1. import _ "github.com/micro/go-plugins/transport/nats"

在启动指令中带上transport参数

  1. go run main.go --transport=nats --transport_address=127.0.0.1:4222

或者直接使用transport函数

  1. transport := nats.NewTransport()


go-micro传输接口:

  1. type Transport interface {
  2. Dial(addr string, opts ...DialOption) (Client, error)
  3. Listen(addr string, opts ...ListenOption) (Listener, error)
  4. String() string
  5. }

Transport

Broker代理

NATS - 图3

broker用于异步消息,它可以应用于大多数消息代理(broker)。NATS, 本质上就是一种异步消息系统,有一点需要注意的是,NATS是不会持久化消息的,尽管这方面在某些场景中稍有不足,但是我们仍然会选择NATS作为消息代理。对于不需持久化的地方,NATS足以支撑高弹性的消息收发架构。

NATS通过主题、信息等等概念提供非常直接的发布与订阅机制。不需要太多复杂的操作。消息可以通过异步发送即遗忘的方式推送出去。订阅者们使用相同的信道名在NATS排成一组队列,这样它们就可以自动均分到订阅者。

NATS - 图4

使用broker插件

引入broker插件

  1. import _ "github.com/micro/go-plugins/broker/nats"

通过指令带入nats

  1. go run main.go --broker=nats --broker_address=127.0.0.1:4222

或者直接使用函数

  1. broker := nats.NewBroker()


broker 接口:

  1. type Broker interface {
  2. Options() Options
  3. Address() string
  4. Connect() error
  5. Disconnect() error
  6. Init(...Option) error
  7. Publish(string, *Message, ...PublishOption) error
  8. Subscribe(string, Handler, ...SubscribeOption) (Subscriber, error)
  9. String() string
  10. }

Broker代理

注册

NATS - 图5

注册是go-micro服务发现的接口。可能你会想到,服务发现怎么使用消息总线?有用吗?事实上是的而且运行非常好。很多人在他们的传输中使用消息总线时会避免使用各种各样独立的发现机制。

这是因为消息总线自身可以通过主题与信道处理路由。主题的定位就是作为服务名并且也可以当成路由key,自动在订阅该主题的服务实例之间进行负载均衡。

Go-micro中服务发现与传输机制是两个独立的概念。任何时间客户端如果要发送请求到其它服务中,从表面上看,它会用名称在注册中心查找服务,找到节点地址后便通过传输(transport)与其进行通信。

一般情况下,大多数的服务发现信息都是存在分布式的键值对中,像zookeeper、etcd或者其它类型的系统。

你应该意识到了,NATS并没有分布式的键值存储能力,所以接下来我们要做的事多少是有点不一样的……

广播查询

广播查询如你所想的那样,服务侦听我们需要的特定主题的广播。任何服务想要服务发现信息就得先创建一个响应主题,该主题便是它所订阅的主题,然后在带有它们回复地址的广播主题中进行查询

由于并不知道到底有多少个服务实例,也不知道有多少个响应会返回回来,所以我们会设置一个上限时间值等待返回的响应。

这是一种为服务发现所创建简单粗暴的分散-收集机制,因为NATS兼具弹性与性能的天生属性,这一机制工作起来一级棒!该机制也间接一种简单的方式来过滤响应时间比较长的服务. 未来我们会改进底层实现。

那么总结一下它是如何工作的:

  • 创建应答主题并订阅
  • 发送带有回复地址的广播主题查询
  • 侦听响应,并在限制时长后取消订阅
  • 汇总响应并返回结果
    NATS - 图6

使用注册插件

引入注册插件

  1. import _ "github.com/micro/go-plugins/registry/nats"

启动时指定注册插件

  1. go run main.go --registry=nats --registry_address=127.0.0.1:4222

或者直接使用注册方法

  1. registry := nats.NewRegistry()


go-micro的注册interface:

  1. type Registry interface {
  2. Register(*Service, ...RegisterOption) error
  3. Deregister(*Service) error
  4. GetService(string) ([]*Service, error)
  5. ListServices() ([]*Service, error)
  6. Watch() (Watcher, error)
  7. String() string
  8. }

注册

Micro NATS的弹性

上面的例子中我们只是特定了单一NATS服务,该服务只在本地运行,而实际场景是会安装NATS集群,这样才能高可用并容错。了解更多NATS集群,请参考:NATS集群

Micro接受使用逗号分开的地址作为标识,可以像上面的例子中那样当参数传入,或者使用环境变量。如果你直接使用的客户端库,也可以以选项的方式在注册(registry)、传输(transport)、代理(broker)初始化时设置主机地址集。

在云原生的架构中,按照我们过去的经验,在每个AZ(availability zones)或每个区域搭建一个集群比较理想。

大多数云提供商在AZ之间有相对低延时(3-5ms),这样就可以毫无问题搭建整个区域的集群。

对于高可用的配置,系统具备AZ容错与更进一步的区域容错能力尤其显得重要。不推荐跨区域的集群。理想情况下,更高级的工具须能管理多集群、多区域的系统。

Micro并不限制运行环境,从一开始其便设计为可在任意环境、任意配置中运行。

从服务注册出发,我们提供一个注册中心,服务集群可以在主机池、一定数量的AZ或者区域中本地化。与NATS集群结合,便可以构建我们所需的高可用架构服务。

NATS - 图7

总结

NATS 作为可伸缩与高性能的消息系统,完美融于微服务生态。我们将它与Micro整合,它在Registry注册插件、Transport传输插件,还有Broker代理插件之中运行得非常好。

我们突出实现这三个插件也是为了说明NATS可以有多灵活。

将Micro构建在NATS之上可认为是Micro强大插件化架构的例子,每一个go-micro包都可以实现、替换且只需要一丁点改动。

未来Micro会有更多的技术示例,下一个最有可能是k8s。

希望本篇可以推动你尝试Micro NATS或者写一些插件给其它系统,贡献到社区。

NATS插件源码:github.com/micro/go-plugins