Go Kit 简介
Go Kit
是一个用于在 Go 中构建微服务的编程工具包。与 Go Micro 不同,它被设计为一个用于导入二进制包的库。
Go Kit
遵循简单的规则,例如:
- 没有全局状态
- 声明式组合
- 显式依赖关系
- 接口即约定
- 领域驱动设计
在 Go Kit
中,您可以找到以下的包:
- 认证 - Basic 认证和 JWT 认证
- 传输 - HTTP、Nats、gRPC 等等。
- 日志记录 - 用于结构化服务日志记录的通用接口。
- 指标 - CloudWatch、Statsd、Graphite 等。
- 追踪 - Zipkin 和 Opentracing。
- 服务发现 - Consul、Etcd、Eureka 等等。
- 断路器 - Hystrix 的 Go 实现。
Go Kit
自上而下构建了三层模型分别是: 分别是 Transport 层、Endpoint 层、Service 层。
- Transport 层:处理 HTTP、gRPC、Thrift 等协议相关的逻辑,主要对请求进行解码、对响应进行编码操作;
- Endpoint 层:在 Service 的上层作为业务的中间件,可使用限流、熔断、监控等能力;
- Service 层:用来处理业务逻辑;
利用三层模型可以构建单体服务 -> 微服务 -> 分布式
编写一个简单的 HTTP 单体服务
首先构造一个 service
, 将服务的建模构造成接口
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
| package services
import ( "errors" "strings" )
var ErrEmpty = errors.New("empty string")
type StringService interface { Uppercase(string) (string, error) Count(string) int }
type StrService struct{}
func (StrService) Uppercase(s string) (string, error) { if s == "" { return "", ErrEmpty } return strings.ToUpper(s), nil }
func (StrService) Count(s string) int { return len(s) }
|
在 Go kit 中,主要的消息传递模式是 RPC。因此,我们接口中的每个方法都将被建模为远程过程调用。对于每个方法,我们定义请求和响应结构,分别捕获所有输入和输出参数。
然后组装 endpoint
端点, 它是外部服务与内部服务的桥梁, 往后可以在这里添加中间件的构造。
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 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
| package transports
import ( "context" "encoding/json" service "kit/services" "net/http"
"github.com/go-kit/kit/endpoint" )
type UppercaseRequest struct { S string `json:"s"` }
type UppercaseResponse struct { V string `json:"v"` Err string `json:"err,omitempty"` }
type CountRequest struct { S string `json:"s"` }
type CountResponse struct { V int `json:"v"` }
func MakeUppercaseEndpoint(svc service.StringService) endpoint.Endpoint { return func(ctx context.Context, request interface{}) (interface{}, error) { req := request.(UppercaseRequest) v, err := svc.Uppercase(req.S) if err != nil { return UppercaseResponse{v, err.Error()}, nil } return UppercaseResponse{v, ""}, nil } }
func MakeCountEndpoint(svc service.StringService) endpoint.Endpoint { return func(ctx context.Context, request interface{}) (interface{}, error) { req := request.(CountRequest) v := svc.Count(req.S) return CountResponse{v}, nil } }
func DecodeUppercaseRequest(_ context.Context, r *http.Request) (interface{}, error) { var request UppercaseRequest if err := json.NewDecoder(r.Body).Decode(&request); err != nil { return nil, err } return request, nil }
func DecodeCountRequest(_ context.Context, r *http.Request) (interface{}, error) { var request CountRequest if err := json.NewDecoder(r.Body).Decode(&request); err != nil { return nil, err } return request, nil }
func EncodeResponse(_ context.Context, w http.ResponseWriter, response interface{}) error { return json.NewEncoder(w).Encode(response) }
|
为服务添加日志中间件: 如果没有彻底的日志记录和检测,任何服务都不能被认为是生产就绪的。
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
| package middlewares
import ( "kit/services" "time"
"github.com/go-kit/log" )
type LoggingMiddleware struct { Logger log.Logger Next services.StringService }
func NewLoggingMiddleware(logger log.Logger, next services.StringService) *LoggingMiddleware { return &LoggingMiddleware{ Logger: logger, Next: next, } }
func (mw LoggingMiddleware) Uppercase(s string) (output string, err error) { defer func(begin time.Time) { mw.Logger.Log( "method", "uppercase", "input", s, "output", output, "err", err, "took", time.Since(begin), ) }(time.Now())
output, err = mw.Next.Uppercase(s) return }
func (mw LoggingMiddleware) Count(s string) (n int) { defer func(begin time.Time) { mw.Logger.Log( "method", "count", "input", s, "n", n, "took", time.Since(begin), ) }(time.Now())
n = mw.Next.Count(s) return }
|
现在我们需要将服务公开,以便调用它。您的组织可能已经对服务应该如何相互通信有了自己的看法。也许你使用Thrift,或者自定义JSON over HTTP。围棋套件支持许多开箱即用的传输工具。
对于这个最小的示例服务,让我们使用HTTP上的JSON。Go kit在package transport/http中提供了一个helper结构。
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
| package main
import ( "net/http" "os"
service "kit/services" transport "kit/transports"
middleware "kit/middlewares"
httptransport "github.com/go-kit/kit/transport/http" "github.com/go-kit/log" )
func main() { logger := log.NewLogfmtLogger(os.Stderr)
svc := middleware.NewLoggingMiddleware(logger, service.StrService{})
uppercaseHandler := httptransport.NewServer( transport.MakeUppercaseEndpoint(svc), transport.DecodeUppercaseRequest, transport.EncodeResponse, )
countHandler := httptransport.NewServer( transport.MakeCountEndpoint(svc), transport.DecodeCountRequest, transport.EncodeResponse, )
http.Handle("/uppercase", uppercaseHandler) http.Handle("/count", countHandler) http.ListenAndServe(":25567", nil) }
|