go-kit开发微服务 - 服务注册与发现
# 介绍
go-kit 内置了多种注册中心支持,包括:
- consul (opens new window)
- dnssrv (opens new window)
- etcd (opens new window)
- eureka (opens new window)
- zookeeper (opens new window)
以下以etcd3为例,实现服务注册与发现功能。
# 实现步骤
# 准备工作
安装 etcd:https://etcd.io/docs/v3.5/install/ (opens new window)
# 服务注册(服务端)
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)
}
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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
启动服务
go run main.go
1
# 服务发现(客户端)
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
}
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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
请求服务端接口
$ go run cli/main.go
2024/04/21 17:33:32 resp: hi: fengjx
1
2
2
Last Updated: 2024/05/12, 15:25:49