简介 - 了解 Go kit 的关键概念

如果您转自Symfony(PHP)、Rails(Ruby)、Django(Python)或许多流行的MVC风格的框架之一,首先应该知道Go-kit不是MVC框架。相反,Go-kit服务分为三层:

  • 传输层 Transport
  • 端点层 Endpoint
  • 服务层 Service

服务在请求时进入第1层后,下传到第3层,响层则采用相反的过程。

这可能是一个小小的调整,但一旦你摸索出了这些概念,你就会发现Go-kit设计非常适合现代软件设计:既有微服务,也有优雅的整体。

传输层(Transports) - 什么是 Go kit transports

传输域需绑定具体的传输协议,如HTTP或gRPC.在微服务中可能支持一个或多个传输,这是非常强大的;您可以支持传统的HTTP API以及较新的RPC服务

在实现RESTful HTTP API时,路由是在HTTP传输中定义的。在HTTP路由器函数中定义的路由最常见如下:

r.Methods("POST").Path("/profiles/").Handler(httptransport.NewServer(
    e.PostProfileEndpoint,
    decodePostProfileRequest,
    encodeResponse,
    options...,
))

Endpoints - 什么是 Go kit endpoints

Endpoints 就像控制器上的操作/处理程序;它是安全和反敏捷逻辑的所在。如果实现两个传输(HTTP和gRPC),则可能有两种向同一端点发送请求的方法。

Services — What is a Go kit service?

服务是实现所有业务逻辑的地方。服务通常将多个端点结合在一起。在Go-kit中,服务通常被建模为接口,这些接口的实现包含业务逻辑。Go-kit服务应努力遵守干净的架构Clean Architecture 或六边形架构 Hexagonal Architecture。也就是说,业务逻辑不应该了解端点,尤其是传输域的概念:您的服务不应该知道任何关于HTTP头或gRPC错误代码的信息。

Middlewares — 在Go kit 中使用中间件(Middleware)?

Go-kit试图通过使用中间件(或装饰者 decorator)模式来强制严格分离关注点。中间件可以包装端点或服务以添加功能,如日志记录、速率限制、负载平衡或分布式跟踪。在一个端点或服务周围链接多个中间件是很常见的。

设计 — Go kit 微服务建模?

Go kit service diagram

把所有这些概念放在一起,我们看到Go-kit微服务的建模就像洋葱,有很多层。这些层可以分为三个域。最里面的服务域是所有东西都基于您的特定服务定义的地方,也是所有业务逻辑都实现的地方。中间端点域是将服务的每个方法抽象为通用endpoint.Endpoint的地方,也是实现安全和反敏捷逻辑的地方。最后,最外面的传输域是端点绑定到具体传输(如HTTP或gRPC)的地方。

通过为服务定义接口并提供具体实现,可以实现核心业务逻辑。然后,编写服务中间件来提供额外的功能,如日志记录、分析、检测—任何需要您的业务领域知识的功能。

Go-kit提供端点和传输域中间件,用于诸如速率限制、电路中断、负载平衡和分布式跟踪等功能,所有这些通常对您的业务域都是不可知的。

简而言之,Go-kit试图严格执行使用中间件(或装饰者)模式进行关注点分离。

依赖注入 (Dependency Injection) — Why is func main always so big?

Go-kit鼓励您将服务设计为多个交互组件,包括多个单用途中间件。经验告诉我们,在微服务中定义和连接组件图的最容易理解、维护和表达的方法是通过在大型函数main中显式和声明性组合。

控制器反转是其他框架的一个常见特性,通过依赖注入或服务定位器模式实现。但是在Go-kit中,应该在func-main中连接整个组件图。这种风格强化了两个重要的优点。通过严格地将组件生命周期保持在main中,可以避免依赖全局状态作为快捷方式,这对可测试性至关重要。如果组件的作用域是main,那么将它们作为依赖项提供给其他组件的唯一方法是将它们显式地作为参数传递给构造函数。这使得依赖性保持显式,从而在启动之前避免了大量技术债务。

例如,假设我们有以下组件:

  • Logger
  • TodoService, implementing the Service interface
  • LoggingMiddleware, implementing the Service interface, requiring Logger and concrete TodoService
  • Endpoints, requiring a Service interface
  • HTTP (transport), requiring Endpoints

您的 func main 应按如下方式连接:

logger := log.NewLogger(...)

var service todo.Service    // interface
service = todo.NewService() // concrete struct
service = todo.NewLoggingMiddleware(logger)(service)

endpoints := todo.NewEndpoints(service) 
transport := todo.NewHTTPTransport(endpoints)

At the cost of having a potentially large func main, composition is explicit and declarative. For more general Go design tips, see Go best practices, six years in.

以拥有一个潜在的大型func main为代价,composition是显式的和声明性的。有关更多通用Go设计技巧,请参阅 Go best practices,6years in

部署(Deployment) — 怎样布署(deploy) Go kit 服务?

It’s totally up to you. You can build a static binary, scp it to your server, and use a supervisor like runit. Or you can use a tool like Packer to create an AMI, and deploy it into an EC2 autoscaling group. Or you can package your service up into a container, ship it to a registry, and deploy it onto a cloud-native platform like Kubernetes.~~

Go kit is mostly concerned with good software engineering within your service; it tries to integrate well with any kind of platform or infrastructure.

这完全取决于你。您可以构建一个静态二进制文件,将其scp到您的服务器,并使用类似runit的管理器。或者可以使用Packer之类的工具创建AMI,并将其部署到EC2自动缩放组中。或者你可以将你的服务打包到一个容器中,将其发送到一个注册中心,并将其部署到像Kubernetes这样的云原生平台上。

Go-kit主要关注服务中的良好软件工程;它试图与任何类型的平台或基础设施很好地集成。

Errors — How should I encode errors?

Your service methods will probably return errors. You have two options for encoding them in your endpoints. You can include an error field in your response struct, and return your business domain errors there. Or, you can choose to return your business domain errors in the endpoint error return value.

Both methods can be made to work. But errors returned directly by endpoints are recognized by middlewares that check for failures, like circuit breakers. It’s unlikely that a business-domain error from your service should cause a circuit breaker to trip in a client. So, it’s likely that you want to encode errors in your response struct.

addsvc contains examples of both methods.

您的服务方法可能会返回错误。有两个选项可以在端点中对它们进行编码。您可以在响应结构中包含错误字段,并在其中返回业务域错误。或者,可以选择在端点错误返回值中返回业务域错误。

两种方法都能奏效。但是,由Endpoint直接返回的错误由检查故障(如断路器)的中间件识别。您的服务中的业务域错误不太可能导致客户端中的断路器跳闸。因此,您可能希望在响应结构中对错误进行编码。

addsvc包含这两种方法的示例。

最后编辑: Simon  文档更新时间: 2021-09-27 12:38   作者:Simon