Go-Kit 介绍
Go Kit 并不是一个框架而是一个包的集合。它可以帮助我们构建健壮、可靠、可维护的微服务,这点在生产环境中已得到验证。所以对于想要实现一个简洁架构的 Go 开发者来说,了解 Go Kit 包是非常必要的。
关键概念
Go Kit 构建的服务可以分为三层:
- Transport (传输层)
- Endpoint (端点层)
- Service (服务层)
Transport

通信协议:提供多种传输协议的支持,包括 HTTP、gRPC、JSON-RPC、CLI 等。
数据编码:负责将数据在服务之间进行编码和解码。不同的通信协议需要不同的编解码方式。
Endpoint

端点:是 Service 层的入口,对 Service 进行 wrapper。它是定义输入和输出定义用例的地方,用简洁架构术语来说,就是处理请求->调用 Service->返回响应。
请注意,端点是一个接收请求并返回响应的函数,它们都是 interface{},即 RequestModel 和 ResponseModel。理论上它也可以用类型参数(泛型)来实现。
config
中间件:通过一组中间件来组合和包装端点,实现例如日志记录、认证、限流等功能
Service

Service 层是实现所有业务逻辑的地方。服务不了解端点,端点和服务都不了解传输域。
Service 有多个 Endpoint 组成,每个 Endpoint 代表了一个具体功能。
简单示例
我们通过一个简单的微服务示例来加深对 Go-Kit 的理解。
假设我们要完成一个打招呼的功能,具体源码可以看 Go-Kit http 微服务示例。
文件结构如下:
| 1
2
3
4
5
6
7
8
9
 | |-sample
  |-endpoint/
    |-endpoint.go
  |-service/
    |-service.go
  |-transport/
    |-transport.go
  |-go.mod
  |-main.go
 | 
- endpoint: 端口层
- service: 业务逻辑层
- transport: 传输层
服务层
服务层的代码非常简单:
|  1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
 | package service
type IServer interface {
    Hello(name string) string
    Bye(name string) string
}
type Server struct {
}
func (s Server) Hello(name string) string {
    return "Hello " + name
}
func (s Server) Bye(name string) string {
    return "Bye " + name
}
 | 
如 Go Kit 所建议的,第一步是为我们的服务创建一个接口,接口有 2 个方法 Hello() 和 Bye() 。Server 结构体实现了这个接口。
另外,从这里我们可以看出,一个服务是可以有多个端点的,端点的代码稍后展示。
端点层
服务层定义了 Hello() 和 Bye() 两个方法,如果要调用这两个方法,我们需要创建两个端点。
|  1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
 | package endpoint
import (
    "context"
    "sample/service"
    "github.com/go-kit/kit/endpoint"
)
// Package
// Imports
// Types
// Constants AND Var
// Type Methods
// type Endpoint func(ctx context.Context, request interface{}) (interface{}, error)
type HelloRequest struct {
    Name string `json:"name"`
}
type HelloResponse struct {
    Msg string `json:"msg"`
}
type ByeRequest struct {
    Name string `json:"name"`
}
type ByeResponse struct {
    Msg string `json:"msg"`
}
func MakeHelloEndpoint(s service.IServer) endpoint.Endpoint {
    return func(ctx context.Context, request interface{}) (interface{}, error) {
        req := request.(HelloRequest)
        msg := s.Hello(req.Name)
        return HelloResponse{Msg: msg}, nil
    }
}
func MakeByeEndpoint(s service.IServer) endpoint.Endpoint {
    return func(ctx context.Context, request interface{}) (interface{}, error) {
        req := request.(ByeRequest)
        msg := s.Bye(req.Name)
        return ByeResponse{Msg: msg}, nil
    }
}
 | 
endpoint.go 文件中,我们分别定义了 Hello 和 Bye 的请求体和返回响应体。
同时我们也定义了 MakeHelloEndpoint() 和 MakeByeEndpoint() 两个函数,目的是将传输层的请求体转为服务层能够识别的结构体,并返回响应。
端点层对传输层一无所知,无论是哪种传输协议,都没有区别。
endpoint.Endpoint 定义如下:
| 1
 | type Endpoint func(ctx context.Context, request interface{}) (response interface{}, err error)
 | 
传输层
在这一层中,我们可以有多种实现,如 HTTP, gRPC, AMPQ 等。
|  1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
 | package transport
import (
    "context"
    "encoding/json"
    "net/http"
    "sample/endpoint"
)
func HelloRequestDecoder(_ context.Context, r *http.Request) (interface{}, error) {
    var request endpoint.HelloRequest
    err := json.NewDecoder(r.Body).Decode(&request)
    return request, err
}
func HelloResponseEncoder(_ context.Context, w http.ResponseWriter, response interface{}) error {
    w.Header().Set("Content-Type", "application/json")
    return json.NewEncoder(w).Encode(response)
}
func ByeRequestDecoder(_ context.Context, r *http.Request) (interface{}, error) {
    var request endpoint.ByeRequest
    err := json.NewDecoder(r.Body).Decode(&request)
    return request, err
}
func ByeResponseEncoder(_ context.Context, w http.ResponseWriter, response interface{}) error {
    w.Header().Set("Content-Type", "application/json")
    return json.NewEncoder(w).Encode(response)
}
 | 
transport.go 文件分别定义了 Hello 和 Bye 的请求和响应的编解码函数。
目的是将传输层中的数据解析到端点层的结构体,将从端点层返回的结构体解析到传输层。
main
main.go 文件中,我们将使用所有层:
|  1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
 | package main
import (
    "fmt"
    "net/http"
    "sample/endpoint"
    "sample/service"
    "sample/transport"
    "time"
    
    httpTransport "github.com/go-kit/kit/transport/http"
)
func main() {
    s := service.Server{}
    
    // 调用端点层
    hello := endpoint.MakeHelloEndpoint(s)
    bye := endpoint.MakeByeEndpoint(s)
    
    // 实例化 http 服务
    helloServer := httpTransport.NewServer(
        hello,
        transport.HelloRequestDecoder,
        transport.HelloResponseEncoder,
    )
    byeServer := httpTransport.NewServer(
        bye,
        transport.ByeRequestDecoder,
        transport.ByeResponseEncoder,
    )
    
    // 开启 2 个协程监听端口 hello 和 bye 服务
    go func() {
        err := http.ListenAndServe(":8081", helloServer)
        if err != nil {
            panic(err)
        }
    }()
    go func() {
        err := http.ListenAndServe(":8082", byeServer)
        if err != nil {
            panic(err)
        }
    }()
    
    for {
        time.Sleep(time.Second)
        fmt.Println(11111)
    }
}
 | 
运行
| 1
2
3
4
 | $ curl -s -XPOST -d'{"name": "Anna"}' localhost:8080/hello
{"msg":"Hello Anna"}
$ curl -s -XPOST -d'{"name": "Anna"}' localhost:8080/bye
{"msg":"Bye Anna"}
 | 
参考:Microservices in Go using the Go kit