Go Micro(5)——架构与微服务的设计模式

Go Micro(5)——架构与微服务的设计模式

有很多关于 micro 架构的疑问和微服务的设计模式的问题,今天我们讨论一下这两个话题。


关于Micro

Micro 是一个微服务工具箱,它有自己固有的设计模式,但插件化的架构可以让底层的实现很轻易的被替换。

micro 专注于定位微服务构建过程中的最基本的需求,并通过精密的设计来满足这些需求。

查看过往的文章可以了解微服务的理念和 Micro 的特性。

关于工具箱

Go Micro 是一个用 golang 编写的,插件化的 RPC 框架。它提供了基础的库,比如服务发现、客户端负载均衡、编解码、同步异步通信等。

Micro API 是一个 API 网关,用于将外部的 HTTP 请求路由到内部的 micro 服务上。它有单一的接入点,可以通过反向代理或者 http 转换成 RPC 来访问。

Micro Web 是一个 web 仪表盘,也是作为 micro web 应用的反向代理。我们相信 web 应用也应该是一个微服务,在微服务世界里也应该是第一等公民。它表现的很像 Micro API 但也有单独的特性比如 websocket

Micro Sidecar 使用 http 服务,提供了 go-micro 的所有特性。虽然我们喜欢 golang 来构建微服务,但你也许想使用其他语言。所以 Sidecar 提供了一种其他语言的应用接入 Micro 世界的方式。

Micro CLI 是一个简单直接的命令行接口,用于与你的服务交互。他也可以使用你的 Sidecar 作为代理来连接服务。

上面是很简单的介绍,下面我们更加深入一些。

RPC,REST,Proto…

第一件你想到的事情是,为什么是 RPC,而不是 REST? 在内部服务间的通信上,我们相信 RPC 是更合适的。或者更明确一点,RPC 使用 protobuf 做编码,通过 protobuf 定义 API。这种方式把两个需求结合起来了:一个需求是需要明确定义的 API 接口,另一个需求是高性能的消息编解码。RPC 是非常直接的通信方式。

我们在这个选择上并不孤独。

Googleprotobuf 的创造者,在内部通过 gRPC 这个框架,大量的使用 RPC 调用。Hailo 也从 RPC/protobuf 的组合中收益很多,不仅是系统性能,开发速度也提高很多。Uber 选择开发自己的RPC框架,名字叫 TChannel

个人而言我认为未来的 API 将会使用 RPC 进行构建,因为它们结构化的格式、高效的编解码提供了定义良好的 API 和高性能的通信。

HTTP to RPC,API…

事实上,我们在 webRPC 还有很长的路要走。在内部 RPC 的表现是完美的,但在面对外部请求比如网站、手机 app 的接口等等,就是另外一回事了。我们需要面对这个,这就是为什么 Micro 需要一个 API 网关,用来接受并转换 http 请求。

API 网关在微服务架构中是一个常见的模式。它作为一个单一的接入点,外部世界的请求,通过它进行路由分发。它让 HTTP API 可以由背后的很多服务所组成。

microAPI 网关使用路径到服务的解决方案,因此不同的请求路径,对应了不同的服务。比如 /user => user api,/order => order api

这里有一个例子。一个请求的路径是 /comstomer/orders,这个请求会被转发到 go.micro.api.customer 这个服务,会使用 Customer.Orders 这个方法进行处理。

image.png

你也许会问,API 服务到底是怎样的?我们下面来讨论一下不同类型的服务。

服务的类型

微服务的关键理念就是业务的拆解,这是从 unix 的设计哲学中得到的启示:『doing one thing and doing it well』,因为这个原因,我们认为不同的服务需要有逻辑上和架构上的区别,以实现自己不同的任务。

我们知道这些理念并没有什么太大的新意,但在一些非常大而且成功的公司,它们的实践取得了成功。我们的目标是传播这些开发理念,并通过工具来进行指导。

目前我们定义了下面的几种服务。

API

通过 micro api 运行,API 服务在你的架构中处于关键位置,大部分作用是接受外部世界的请求并分发到内部的服务上。你可以通过 micro api 提供的反向代理 REST 模式进行访问,也可以通过 RPC 接口进行访问。

WEB

通过 micro web 运行,web 服务专注于服务 html 请求,构建仪表盘。micro web 反向代理 httpwebsocket,目前只有这两种协议支持,未来也许会增加。

SRV

这是后台的 RPC 服务,他们的目标是为你的系统提供核心的功能,大部分并不是公开的接口。你仍然可以通过 micro apimicro web,使用 /rpc 接入点进行访问。这种接入方式直接使用 go-microclient 进行调用。

image.png

按照过去的经验,我们发现这样的架构设计非常强大。可以被扩展到数以百计的服务。通过把它整合到 Micro 架构中,我们发现它为微服务的开发提供了非常好的基础。

Namespacing

你也许会想,怎样区分 micro api 或者 micro web 以及服务呢。我们通过命名空间进行拆分。通过命名的前缀,我们可以很清晰的看到,某个服务是哪种类型的。这很简单但很高效。

micro api 会把 /customer 这样的请求路径定位到 go.micro.api.customer 服务。

默认的命名空间是:

  • API - go.micro.api
  • WEB - go.micro.web
  • SRV - go.micro.srv

你应该把它设置成你的域名,比如 com.example.api。这些都可以进行配置。


同步和异步

你经常听说微服务是很灵活的模式。大多数来说,微服务是关于创造事件驱动的架构,以及设计通过异步通信的方式响应的服务。

Micro 把异步通信作为微服务构建中的第一等公民。事件通过异步消息,可以被任何人消费并作出反应,搭建一个新服务不需要对系统的其他部分作出任何更改。这是一种强大的设计模式,因为这个原因,我们在 go-micro 中定义了 Broker 接口。

image.png

异步和同步通信在Micro中是分离开的。Transport 接口用于构建服务之间的点对点的通信。go-micro 中的 clientserver 基于 transport 来进行请求和返回RPC调用,提供了双向的通信流。

image.png

在构建系统时,两种通信方式都应该使用,但关键是理解在什么场景下应该用什么类型的通信方式。在大部分情况下并没有好坏之分,我们需要权衡处理。

一个 broker 和异步通信的典型使用方式是这样:监听系统通过 broker 对服务的事件历史进行记录。

image.png

在这个例子中,每个服务的每个 API 在被调用时,都会把事件上报到监听 topic,监听系统会订阅这个 topic,并把他们存储到时间序列的数据库中。在 admin 管理平台可以看到任何用户的操作历史。

如果我们通过同步通信做,监听系统直接面对巨大的请求数。如果监听系统宕机了,我们就丢失了这些数据。通过把这些事件发布到 broker,我们可以异步的持久化这些数据。这是一种微服务中常见的事件驱动设计模型。

我们怎样定义微服务?

我们已经讨论了很多 Micro 能为微服务提供的工具箱,也定义了服务的类型。但还没有真正讨论,到底什么是微服务。

微服务与其他应用的区别到底在哪里,微服务为什么叫微服务。

现在有很多不同的定义,但有两条适合大部分微服务系统。

Loosely coupled service oriented architecture with a bounded context

An approach to developing a single application as a suite of small services, each running in its own process and communicating with lightweight mechanisms

微服务的哲学与 unix 也类似

Do one thing and do it well

我们认为微服务是这样一种应用程序:专注于单一的业务,并通过明确定义的API对外提供服务。

看看我们在社交网络中怎样使用微服务:

image.png

其中一种是流行的 MVC 架构,在 MVC 世界中,每个实体代表了一个模型,模型又是作为数据库的抽象。模型之间也许有一对多或者多对多的关系。controller 模块负责处理请求,接受 model 模块返回的数据,并把数据传输到 view 层,进行渲染,最后输出给用户。

在微服务架构中,面对同样的例子。每个模型实际上是一个服务,通过 API 进行服务间通信。用户请求,数据的集合以及渲染是通过一系列不同的 web 服务进行处理的。每个服务有自身的专注点,当我们需要增加一个新特性时,我们只需要把关联的服务进行修改,或者直接写一个新的服务。分离的理念提供了大规模开发的模式。

版本

image.png

开发真实世界的软件时,版本是非常重要的。在微服务世界里,严格的把 API 和业务逻辑分离到许多不同的服务上,因为这个原因,服务的版本控制是核心的工具的很重要的一部分。可以让我们在流量很大时也能进行升级。

go-micro 中,服务定义了名字和版本,Registry 模块返回服务的列表,根据版本把节点进行了区分。这里是 service 的接口定义。

type Service struct {
    Name      string
    Version   string
    Metadata  map[string]string
    Endpoints []*Endpoint
    Nodes     []*Node
}

版本控制需要与 Selector 结合起来,selector 是客户端的负载均衡机制,通过 selector 的策略实现请求根据版本进行分发。

selector 是非常强大的接口,我们根据不同的路由算法,比如随机、轮询、根据标签、响应时间等等。

通过使用默认的随机负载算法,再加上版本控制算法,我们就可以进行灰度发布。

image.png

在未来,我们会尝试实现一个全局的负载策略,根据历史的趋势进行选择,可以根据版本,设置不同的百分比,并动态的为服务增加标签。

大规模扩展

上面的介绍的版本系统,是大规模扩展服务时的基本模式。register 存储了服务的注册信息,我们通过 selector 实现了路由和负载均衡。

按照 doing one thing well 的理念,扩展架构也应该是简单、明确定义的 API、分层次的架构。通过创造这些工具,我们可以构建更加可靠的系统,专注于更高级别的业务需求。

这是 Micro 编写的基础理念,也是我们希望微服务开发者遵循的理念。

当我们在生产环境部署应用时,我们就需要构建可扩展、高容错、高性能的应用。云计算让我们可以进行不受限制的扩展,但是没有任何东西会一直正常运行。事实上,在构建分布式系统中,怎样对待运行失败的服务是非常重要的一方面,你在构建你的系统时,需要好好考虑。

在云计算的世界,我们想要在数据中运行错误,甚至多个数据中心运行错误的情况下,也能正常提供服务。在过去我们讨论的是冷热备份,或者是灾难恢复计划。在今天,最先进的技术公司,在全世界不停歇的运作,每个程序都会有多个备份,运行在世界上不同的数据中心。

我们需要向 google,facebook,netflix 和 Twitter 学习,即使在数据中心运行失败时,也要对用户提供服务,在多个数据中心运行失败时,也需要尽快恢复。

Micro 可以让你构建这样的应用,通过插件化的架构,我们可以为不同的分布式系统,实现不同的工具箱。

服务发现和注册器是 Micro 的关键模块,它们可以用于发现在数据中心中的一系列服务,Micro API 可以用于路由和负载一系列的服务。

[图片上传中...(image.png-ffb75d-1513663516734-0)]


总结

希望这篇文章清晰的讲解了Micro的架构,以及怎样实现可扩展的微服务设计模式。微服务首先是一种软件设计模式,我们可以通过工具实现基础、核心的功能,同时也能灵活组合其他设计模式。

因为 Micro 是一个插件化的架构,它强大的能力,可以实现不同的设计模式,在不同的场景中都能使用。比如你构建一个视频流的服务,你也许需要基于 http 的点对点服务。如果你对性能不敏感,你也许需要使用消息队列比如 NATSRabbitMQ

使用Micro这样的工具进行开发是非常让人兴奋的。

推荐阅读更多精彩内容