介绍

go-kit的分层设计可以看成是一个洋葱,有许多层。这些层可以划分为我们的三个领域。

  • Service: 最内部的服务领域是基于你特定服务定义的,也是所有业务逻辑实现的地方
  • Endpoint: 中间的端点领域是将你的每个服务方法抽象为通用的 endpoint.Endpoint,并在此处实现安全性和反脆弱性逻辑。
  • Transport: 最外部的传输领域是将端点绑定到具体的传输方式,如 HTTP 或 gRPC。

可以通过定义一个接口并提供具体实现来实现核心业务逻辑。然后通过编写中间件来组合额外的功能,比如日志记录、监控、权限控制等。

go-kit 内置了一些的中间件

这些中间件可以与你的业务逻辑组合起来实现功能复用。你也可以自己实现中间件,为项目提供基础能力。

中间件在设计模式中属于装饰器模式,实现了关注点分离,代码逻辑清晰,可扩展性强。

实现步骤

我们在第一章greetsvc的基础上增加一个内置basic认证中间件,再自定义一个中间件打印请求耗时。

githu: https://github.com/fengjx/go-kit-demo/tree/master/greetmiddleware

参考代码

package main

import (
    "context"
    "encoding/json"
    "fmt"
    "log"
    "net/http"
    "time"

    "github.com/go-kit/kit/auth/basic"
    "github.com/go-kit/kit/endpoint"
    httptransport "github.com/go-kit/kit/transport/http"
)

func main() {

    svc := greetService{}

    // 中间件组合实现功能扩展
    helloEndpoint := makeHelloEndpoint(svc)
    helloEndpoint = basic.AuthMiddleware("foo", "bar", "hello")(helloEndpoint)
    helloEndpoint = timeMiddleware("hello")(helloEndpoint)

    satHelloHandler := httptransport.NewServer(
        helloEndpoint,
        decodeRequest,
        encodeResponse,
        httptransport.ServerBefore(func(ctx context.Context, request *http.Request) context.Context {
            authorization := request.Header.Get("Authorization")
            // 增加一个拦截器
            ctx = context.WithValue(ctx, httptransport.ContextKeyRequestAuthorization, authorization)
            return ctx
        }),
    )

    http.Handle("/say-hello", satHelloHandler)
    log.Println("http server start")
    log.Fatal(http.ListenAndServe(":8080", nil))
}

type helloReq struct {
    Name string `json:"name"`
}

type helloResp struct {
    Msg string `json:"msg"`
}

type greetService struct {
}

func (svc greetService) SayHi(_ context.Context, name string) string {
    return fmt.Sprintf("hi: %s", name)
}

func makeHelloEndpoint(svc greetService) endpoint.Endpoint {
    return func(ctx context.Context, request interface{}) (interface{}, error) {
        req := request.(*helloReq)
        msg := svc.SayHi(ctx, req.Name)
        return helloResp{
            Msg: msg,
        }, nil
    }
}

func decodeRequest(_ context.Context, r *http.Request) (interface{}, error) {
    name := r.URL.Query().Get("name")
    req := &helloReq{
        Name: name,
    }
    return req, nil
}

func encodeResponse(_ context.Context, w http.ResponseWriter, response interface{}) error {
    data := map[string]any{
        "status": 0,
        "msg":    "ok",
        "data":   response,
    }
    return json.NewEncoder(w).Encode(data)
}

// timeMiddleware 自定义中间件,打印耗时
func timeMiddleware(endpointName string) endpoint.Middleware {
    return func(next endpoint.Endpoint) endpoint.Endpoint {
        return func(ctx context.Context, request interface{}) (interface{}, error) {
            start := time.Now()
            defer func() {
                duration := time.Since(start)
                log.Printf("%s take %v\r\n", endpointName, duration)
            }()
            return next(ctx, request)
        }
    }
}

启动服务

go run main.go

测试

认证失败

# sya-hello
curl http://localhost:8080/say-hello?name=fengjx

认证成功

curl -H 'Authorization: Basic Zm9vOmJhcg==' http://localhost:8080/say-hello?name=fengjx
# server 日志输出
2024/04/21 12:34:37 hello take 6.542µs

铁匠
124 声望9 粉丝

[链接]