Golang 微服务化 (2) - GoKit 构建简单应用

软件基础组成部分

Service, Endpoint, Transport

  1. service 核心逻辑: interface 定义 + struct 实现
  2. 定义 Protobuffer 后自动生成 或者 手动定义 请求返回数据结构
  3. 将请求返回数据结构封装成 Endpoint, 其中数据处理使用定义好的 service 核心逻辑
  4. 将 Endpoint 封装一层 http, grpc… 协议 定义 Transport, 可以供外部请求连接访问

定义 service

service.go

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
import (
"context"
"errors"
"strings"
)

type StringService interface {
Uppercase(string) (string, error)
Count(string) int
}

var ErrEmpty = errors.New("Empty String")

type stringService struct {}

func (stringService) Uppercase(s string) (string, error) {
if s == "" {
return "", ErrEmpty
}

return strings.ToUpper(s), nil
}

func (stringService) Count(s string) int {
return len(s)
}

定义 Protobuf 请求返回数据结构

编写 Protobuf 文件 service.proto

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
syntax = "proto3";

package main;

service StringHandler {
rpc Uppercase(UppercaseRequest) returns (UppercaseResponse) {}
rpc Count(CountRequest) returns (CountResponse) {}
}

message UppercaseRequest { string s = 1; }

message UppercaseResponse {
string v = 1;
string err = 2;
}

message CountRequest { string s = 1; }

message CountResponse {
int64 v = 1;
string err = 2;
}

使用 Protobuf 生成对应的 pb.go 文件

1
protoc service.proto --go_out=.

将 service 和 proto 封装成 Endpoint, 将 Endpoint 封装 http 协议构建 Http Transport

编写 transport.go 文件

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
import (
"context"

"github.com/go-kit/kit/endpoint"
)

// Endpoint

func makeUppercaseEndpoint(svc StringService) endpoint.Endpoint {
return func(_ context.Context, request interface{}) (interface{}, error) {
req := request.(UppercaseRequest)
v, err := svc.Uppercase(req.S)
if err != nil {
return UppercaseResponse{V: v, Err: err.Error()}, nil
}
return UppercaseResponse{V: v, Err: ""}, nil
}
}

func makeCountEndpoint(svc StringService) endpoint.Endpoint {
return func(_ context.Context, request interface{}) (interface{}, error) {
req := request.(CountRequest)
v := svc.Count(req.S)
return CountResponse{V: v, Err: ""}, nil
}
}

// Http Transport

func decodeUppercaseRequest(_ context.Context, r *http.Request) (interface{}, error) {
var request UppercaseRequest
if err := json.NewDecoder(r.Body).Decode(&request); err != nil {
return nil, err
}
return request, nil
}

func decodeCountRequest(_ context.Context, r *http.Request) (interface{}, error) {
var request CountRequest
if err := json.NewDecoder(r.Body).Decode(&request); err != nil {
return nil, err
}
return request, nil
}

func encodeResponse(_ context.Context, w http.ResponseWriter, response interface{}) error {
return json.NewEncoder(w).Encode(response)
}

启动 Http 服务加载 Http Transport

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
import (
"context"
"encoding/json"
"log"
"net/http"

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

func main() {
svc := GetStringService()

uppercaseHandler := httptransport.NewServer(
makeUppercaseEndpoint(svc),
decodeUppercaseRequest,
encodeResponse,
)

countHandler := httptransport.NewServer(
makeCountEndpoint(svc),
decodeCountRequest,
encodeResponse,
)

http.Handle("/uppercase", uppercaseHandler)
http.Handle("/count", countHandler)
log.Fatal(http.ListenAndServe(":8080", nil))
}

启动服务

以上所有文件, 都隶属于 package main.

启动服务

1
go run .

请求测试

1
2
curl -XPOST -d'{"s":"hello, world"}' localhost:8080/uppercase
curl -XPOST -d'{"s":"hello, world"}' localhost:8080/count
Donate - Support to make this site better.
捐助 - 支持我让我做得更好.