Go Micro(4)——基于消息队列NATS构建微服务

Go Micro(4)——基于消息队列NATS构建微服务

这篇文章我们会讨论基于 NATS 使用 Micro。讨论包括了服务发现,同步通信和异步通信。


NATS是什么?

NATS 是一个开源的消息系统,或者说消息队列。NATS 的作者是 Derek Collison, Apcera 的作者。它起源于 VMWare,最开始是一个 ruby 的系统。后来使用 golang 进行重写,逐步的成为了一个高扩展性的高性能消息系统。


为什么是NATS?

为什么不是呢?过去我使用过很多消息队列,很明显 NATS 是鹤立鸡群的,在过去消息系统似乎成了企业的灵丹妙药了,结果是每个人参与的每个系统都离不开消息系统。导致了很多无效的承诺、高昂的研发投入,制造出比它解决的更多的麻烦。

NATS,相比之下,就十分专注,在难以置信的简洁之下,解决了性能问题,解决了高可用行问题。它的口号是『always on and available』,使用了一种『fire and forget』的消息模式。它简单、专注、轻量的特性使它在微服务的候选中脱颖而出。我们相信,在服务间的消息传递领域,它很快会变成主要的候选者。

NATS能提供什么?

  • 高性能、高扩展
  • 高可用
  • 极致的轻量级
  • 一次部署

NATS不支持什么?

  • 持久化
  • 事务
  • Enhanced delivery modes
  • Enterprise queueing

NATS 是否适合 Micro 呢?我们接着讨论


Micro on NATS

Micro 采用插件化的架构设计,用户可以替换底层的实现,而不更改任何底层的代码。每个 Go-Micro 框架的底层模块定义了相应的接口,registry 是作为服务发现,transport 作为同步通信,broker 作为异步通信。

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

type Registry interface {
    Register(*Service, ...RegisterOption) error
    Deregister(*Service) error
    GetService(string) ([]*Service, error)
    ListServices() ([]*Service, error)
    Watch() (Watcher, error)
    String() string
}

type Broker interface {
    Options() Options
    Address() string
    Connect() error
    Disconnect() error
    Init(...Option) error
    Publish(string, *Message, ...PublishOption) error
    Subscribe(string, Handler, ...SubscribeOption) (Subscriber, error)
    String() string
}

为每个组件创建一个插件很简单,只需要实现这些组件的接口即可。我们会在未来的文章里详细讨论,怎样写插件,如果你想详细了解 NATS 的插件或者其他的类似 etcd 作为服务发现,kafka 作异步通信,rabbitmq 作同步通信,在这里可以看到: github.com/micro/go-plugins

Micro on NATS 本质上是实现一系列的插件,将 NATS 消息系统整合到 Micro 的框架中来。通过为 Go Micro 提供不同的插件,我们可以创造出很多灵活的组合。

在实践过程中,不能一套系统打天下,Micro on NATS 的可以灵活的定义各种模型。

下面我们会讨论一下 NATS 插件怎样在 transportbrokerregistry 下工作。

Transport

[图片上传失败...(image-a3bc83-1513577247790)]

transportgo-micro 定义的同步通信接口,它使用了泛型的语法描述,类似其他 golang 代码,比如 Liesten,Dial,Accept。这些理念和模式在类似 TCPHTTP 这样的同步通信中很容易理解,但是在消息系统中要怎样兼容呢?通信过程中,连接是与消息队列建立的,而不是服务本身。为了达到目的,我们使用消息系统中的 topicschannels 来创造虚连接。

它是怎样工作起来的?

一个服务使用 transport.Listen 来监听消息,这会与 NATS 之间建立连接。当 transport.Accept 被调用时,一个唯一的 topic 会被创建并订阅。这个唯一的 topic 地址会作为服务的地址,注册到 go-micro 的注册器中。每个收到的消息会被虚连接处理,如果一个连接已经存在,而且回复的地址是一样的,我们会简单的把消息积压在连接上。

客户端通过 transport.Dial 来连接服务端,其实它是建立了与 NATS 的连接,然后创建了唯一的 topic 并且订阅这个 topic。这个 topic 用于接收服务端的返回。任何时候客户端发送消息到服务端时,它都会把回复地址设置成这个 topic。(译注:服务端的地址是固定的,而客户端的每个请求,都会生成一个客户端地址,唯一的请求与唯一的地址实现一一关联)

当任何一边想要关闭连接时,简单的调用 transport.Close 就会断开与 NATS 的连接。

[图片上传失败...(image-a17b4e-1513577247790)]

使用 transport 插件

引用插件

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

启动时传入参数

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

或者直接在代码中创建

transport := nats.NewTransport()

go-microtransport 接口:

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

Broker

[图片上传失败...(image-14ecc9-1513577247790)]

brokego-micro 的异步通信接口,它定义了泛型的、高等级的通用接口。NATS 本身作为消息系统,是可以作为消息 broker 的。这里只有一个警告:nats 并不持久化消息。虽然在某些场景有风险,但我们相信 NATS 可以也应该用于 go-microbroker。持久化并不是必须的,它提供了高扩展的发布和订阅架构。

NATS 通过 TopicsChannels 的理念,非常直接的实现了发布和订阅机制。这里并没有其他工作需要做。消息可以被发布到任何异步的 fire and forget manner。通过 NATSQueue Group,订阅方可以通过 channel 的名字就行订阅。实现消息自动的分发的多个订阅者。

[图片上传失败...(image-be955d-1513577247790)]

使用 broker 插件

引入包

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

(译注:注意此时引用的是 broker 中的 nats 包,上面引用的是 transport 中的 nats 包)

通过参数启动

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

也可以直接启动

broker := nats.NewBroker()

go-microbroker 需要实现的接口:

type Broker interface {
    Options() Options
    Address() string
    Connect() error
    Disconnect() error
    Init(...Option) error
    Publish(string, *Message, ...PublishOption) error
    Subscribe(string, Handler, ...SubscribeOption) (Subscriber, error)
    String() string
}

Register

[图片上传失败...(image-f5229e-1513577247790)]

Registergo-micro 定义的服务发现接口,你也许想过。用消息系统做服务发现?这能工作吗?事实上它不仅能工作,还工作的很好。许多人在服务发现中使用消息系统,避免了不一致的发现机制。这是因为消息队列通过 topicschannels 实现了路由,Topics 定义为服务的名字可以作为路由的 key,订阅 topics 的服务自动实现了负载均衡。

Go-micro 把服务发现和传输机制看做两个不同的领域,任何时候,客户端向服务发起请求,都会首先在注册器中根据服务的名字查询到正在运行的服务节点,然后客户端通过 transport 与节点进行通信。

一般来说,最常见的存储服务发现信息的方式是通过分布式 key-value store,比如 zookeeper、etcd 等等。正如你了解到的,NATS 不是一个分布式的 KV store,我们将做一下改动。。。

广播查询!

广播查询正如你想象的那样,服务都会监听一个特点的 topic,任何需要服务发现信息的都需要订阅这个 topic,然后对这个 topic 做一个反馈。

因为我们并不知道有多少服务正在运行,我们对响应时间设置一个上限。这是一个粗糙的服务发现机制,但因为 NATS 本身的高扩展性和高性能,它运行的反而非常好。它也间接的提高了过滤服务节点的响应时间。未来我们会试着提高底层的实现。

我们总结一下它是怎么工作的:

  • 创建一个 reply topic 并订阅
  • 向广播 topic 发起查询,附带上 reply topic
  • 监听回复,超过限制时间就注销订阅
  • 聚合响应并返回结果

[图片上传失败...(image-9adecd-1513577247790)]

使用 register 插件

引入包

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

通过参数启动

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

或者直接在代码中设置

registry := nats.NewRegistry()

go-micro 中的 register 接口

type Registry interface {
    Register(*Service, ...RegisterOption) error
    Deregister(*Service) error
    GetService(string) ([]*Service, error)
    ListServices() ([]*Service, error)
    Watch() (Watcher, error)
    String() string
}

大规模在 Micro 中使用 NATS

在上面的例子中,我们只是用了单个的 NATS 服务,但是在实际情况下,我们一般都是使用 NATS 集群来实现高可用和容错。你可以在这里看看更多 NATS 集群的知识。

Micro 在启动时可以接收一系列的参数,如环境变量等。如果直接使用 client 的库,也可以在创建时指定参数。

在目前云计算的架构下,我们过去的经验是,每个 AZ 都应该有集群。大部分的云计算公司,不同的 AZ 之间的延迟大概是3到5毫秒,集群的通信是没有任何问题的。当我们运行一个高可用的配置,你的系统必须要能在 AZ 宕机、甚至整个 region 崩溃时还能提供服务。我们不建议跨 region 部署集群。理想的高等级工具应该是用于管理多集群和多 region 系统。

Micro 是一个极其灵活的微服务系统,它本身就被设计成运行在任何配置的任何地方。整个 Micro 世界是通过服务发现进行指导,服务的集群可以运行在机器集群中,AZ 或者 regions。再考虑到 NATS 集群,它可以让你构建高可用的服务。

[图片上传失败...(image-92f6b7-1513577247790)]

总结

NATS 是一个高可用和高性能的消息系统,在我们的微服务生态系统中运行的很好。它与 Micro 配合的非常的好,我们可以把它用在 register,transport,broker 中,我们也已经全部实现了这些插件。

NATSMicro 中的使用只是一个示例,它展示了 Micro 高度的插件化架构,每个 go-micro 的包都可以被替换,底层不需要改动代码,上层也只需要改动非常少的代码。在未来我们还会看到更多的 Micro on X,下一个可能是 Micro on kubernetes

希望这可以鼓励你来使用 Micro on NATS,或者编写自己的插件并回馈给社区。

在这里看更多的NATS插件 github.com/micro/go-plugins

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 151,829评论 1 331
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 64,603评论 1 273
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 101,846评论 0 226
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 42,600评论 0 191
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 50,780评论 3 272
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 39,695评论 1 192
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,136评论 2 293
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 29,862评论 0 182
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,453评论 0 229
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 29,942评论 2 233
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,347评论 1 242
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 27,790评论 2 236
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,293评论 3 221
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,839评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,448评论 0 181
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 34,564评论 2 249
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 34,623评论 2 249

推荐阅读更多精彩内容