go-kit學習指南 - 中間件

原文:https://blog.fengjx.com/pages/d6f092

介紹

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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章