go-kit連接配接位址:https://github.com/go-kit/kit
簡介
go-kit本身不是一個架構,而是一套微服務工具集, 它可以用來解決分布式系統開發中的大多數常見問題. 是以你可以專注于你的業務邏輯中
利用它提供的api和規範可以開發出健壯的,維護性高德微服務。
關注重點
Rate Limiter 限流器
Trasport 資料傳輸(序列化和反序列化)
Logging 日志
Metrics 名額
Circuit breaker 斷路器
Request tracing 請求追蹤
Service Discovery 服務發現
go-kit三層架構
-
Transport:通訊實作
主要負責與http,gRPC,thrift等相關的邏輯
-
Endpoint:節點
定義Request和Response格式,并可以使用裝飾器包裝函數,以此來實作各種中間件嵌套。
- Service:業務層
一、簡單栗子
服務端
UserServices.go —》業務邏輯
//建立一個接口,在endpoint中需要用到
type IUserService interface {
GetName(userid int) string
}
//實作這個接口
type UserService struct {
}
//這裡需要實作接口的方法
func (u *UserService) GetName(userid int) string {
if userid == 1 {
return "wd"
}
return "wyh"
}
UserEndpoint.go —》節點實作
import (
"context"
"github.com/go-kit/kit/endpoint"
)
//聲明請求結構體
type UserRequest struct {
Uid int `json:"uid"`
}
//聲明傳回結構體
type UserResponse struct {
Result string `json:"result"`
}
//使用閉包的方式生成一個Endpoint節點
func GetUserEnpoint(userserver IUserService) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (response interface{}, err error) {
r := request.(*UserRequest)
return UserResponse{
Result: userserver.GetName(r.Uid),
}, nil
}
}
UserTransport.go —》傳輸方式,這裡使用http通信,以後的内容會貼出gRPC方式
import (
"context"
"encoding/json"
"errors"
"net/http"
"strconv"
)
//解碼
func DecodeUserRequest(ctx context.Context, r *http.Request) (interface{}, error) {
//http://localhost:8080/?uid=""
if r.URL.Query().Get("uid") != "" {
uid, _ := strconv.Atoi(r.URL.Query().Get("uid"))
return &UserRequest{
Uid: uid,
}, nil
}
return nil, errors.New("參數錯誤")
}
//編碼
func EncodeUserResponse(ctx context.Context, w http.ResponseWriter, response interface{}) error {
//w.Header().Set("Content-Type", "application/json;charset=utf-8")
return json.NewEncoder(w).Encode(response)
}
main.go —》主函數,注意我将上面三個腳本都放在 service 包裡,吾懶,吾自知也,爾等勿學。
import (
"net/http"
httptransport "github.com/go-kit/kit/transport/http"
"github.com/service"
)
func main() {
//ctx := context.Background()
//聲明一個實作了接口的結構體
userserver := &service.UserService{}
//生成一個Endpoint,getnamehandler是go-kit自帶的包,這裡是http傳輸方式的固定寫法,注意不要放入ctx。
getnamehandler := httptransport.NewServer(
service.GetUserEnpoint(userserver),
service.DecodeUserRequest,
service.EncodeUserResponse,
)
http.Handle("/getname", getnamehandler)
http.ListenAndServe(":8080", nil)
}
測試
啟動服務,這裡使用apizza測試,uid=1,傳回{“result”: “wd”}
這裡使用apizza測試,uid=2 傳回{“result”: “wyh”}
二、中間件和日志
中間件
這裡使用裝飾者模式,建立一個日志子產品
UserMiddleware.go
import (
"context"
"github.com/go-kit/kit/endpoint"
"github.com/go-kit/kit/log"
)
//聲明一個中間件類型
type Middleware func(endpoint.Endpoint) endpoint.Endpoint
//為這個中間件實作方法,這裡再開始和結束列印日志
func LogMiddleware(logger log.Logger) Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
logger.Log("msg", "call start")
defer logger.Log("msg", "call end")
return next(ctx, request)
}
}
}
main.go —> 将endpoint 聲明替換如下
logger := log.NewLogfmtLogger(os.Stderr)
userendpoint := service.GetUserEnpoint(userserver)
userendpoint = service.LogMiddleware(log.NewSyncLogger(logger))(userendpoint)
getnamehandler := httptransport.NewServer(
userendpoint,
service.DecodeUserRequest,
service.EncodeUserResponse,
)
測試
再次使用apizza發起請求
控制台列印如下
未完待續