介绍

go-kit 内置了多种注册中心支持,包括:

以下以etcd3为例,实现服务注册与发现功能。

实现步骤

准备工作

安装 etcd:https://etcd.io/docs/v3.5/install/

服务注册(服务端)

package main

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

    "github.com/go-kit/kit/endpoint"
    kitlog "github.com/go-kit/kit/log"
    "github.com/go-kit/kit/sd/etcdv3"
    httptransport "github.com/go-kit/kit/transport/http"
)

func main() {
    var (
        etcdServer = "192.168.1.200:2379"
        prefix     = "/services/greetsv/"
        instance   = "192.168.1.164:8080"
        key        = prefix + instance
        value      = "http://" + instance
        ctx        = context.Background()
    )
    // 创建 etcd 客户端连接
    options := etcdv3.ClientOptions{
        DialTimeout:   time.Second,
        DialKeepAlive: time.Second * 30,
    }
    cli, err := etcdv3.NewClient(ctx, []string{etcdServer}, options)
    if err != nil {
        log.Panic(err)
    }
    // 创建日志记录器
    var logger kitlog.Logger
    logger = kitlog.NewLogfmtLogger(os.Stderr)
    logger = kitlog.With(logger, "ts", kitlog.DefaultTimestampUTC)
    // 创建注册与发现中间件
    r := etcdv3.NewRegistrar(cli, etcdv3.Service{
        Key:   key,
        Value: value,
    }, logger)
    r.Register()
    defer r.Deregister()

    svc := greetService{}
    satHelloHandler := httptransport.NewServer(
        makeHelloEndpoint(svc),
        decodeRequest,
        encodeResponse,
    )

    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)
}

启动服务

go run main.go

服务发现(客户端)

package main

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

    "github.com/go-kit/kit/endpoint"
    kitlog "github.com/go-kit/kit/log"
    "github.com/go-kit/kit/sd"
    "github.com/go-kit/kit/sd/etcdv3"
    "github.com/go-kit/kit/sd/lb"
    httptransport "github.com/go-kit/kit/transport/http"
)

func main() {
    var (
        etcdServer = "192.168.1.200:2379"
        prefix     = "/services/greetsvc/"
        ctx        = context.Background()
    )
    // 创建 etcd 客户端连接
    options := etcdv3.ClientOptions{
        DialTimeout:   time.Second,
        DialKeepAlive: time.Second * 30,
    }
    cli, err := etcdv3.NewClient(ctx, []string{etcdServer}, options)
    if err != nil {
        log.Panic(err)
    }

    logger := kitlog.NewNopLogger()
    instancer, err := etcdv3.NewInstancer(cli, prefix, logger)
    if err != nil {
        panic(err)
    }

    factory := func(instance string) (endpoint.Endpoint, io.Closer, error) {
        u, err := url.Parse(instance)
        if err != nil {
            return nil, nil, err
        }
        httpcli := httptransport.NewClient(http.MethodPost, u, encodeRequest, decodeResponse)
        return httpcli.Endpoint(), nil, nil
    }

    endpointer := sd.NewEndpointer(instancer, factory, logger)
    balancer := lb.NewRoundRobin(endpointer)
    retry := lb.Retry(3, 3*time.Second, balancer)

    req := &helloReq{
        Name: "fengjx",
    }
    resp, err := retry(ctx, req)
    if err != nil {
        log.Fatal(err)
    }
    log.Printf("resp: %v", resp)
}

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

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

type Data[T any] struct {
    Status int    `json:"status"`
    Msg    string `json:"msg"`
    Data   T      `json:"data"`
}

func encodeRequest(_ context.Context, r *http.Request, request interface{}) error {
    req := request.(*helloReq)
    r.Method = http.MethodGet
    r.URL.Path = "/say-hello"
    q := r.URL.Query()
    q.Set("name", req.Name)
    r.URL.RawQuery = q.Encode()
    return nil
}

func decodeResponse(_ context.Context, response *http.Response) (interface{}, error) {
    if response.StatusCode != 200 {
        return nil, fmt.Errorf("response code: %d\r\n", response.StatusCode)
    }
    data := &Data[helloResp]{}
    err := json.NewDecoder(response.Body).Decode(data)
    if err != nil {
        return nil, err
    }
    return data.Data.Msg, nil
}

请求服务端接口

$ go run cli/main.go
2024/04/21 17:33:32 resp: hi: fengjx

铁匠
124 声望9 粉丝

[链接]