不拒绝英文的朋友请移步https://gokit.io/examples/stringsvc.html
第一步
让我们创建一个最小化的gokit 服务,现在我们将用main.go一个文件实现,gokit的首要准则。
业务逻辑
service从业务逻辑开始,为业务逻辑服务,在gokit中我们把服务抽象为一个接口。
// StringService provides operations on strings.
import "context"
type StringService interface {
Uppercase(string) (string, error)
Count(string) int
}
现在给借口 一个实现
import (
"context"
"errors"
"strings"
)
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)
}
// ErrEmpty is returned when input string is empty
var ErrEmpty = errors.New("Empty string")
请求和响应
在gokit中,主流的消息传送方式是RPC,也推荐大家使用RPC。所以,根据rpc的规定,每一个我们借口中的每一个方法都将被建模为远程过程调用。我们定义request贺response的结构,去捕获对应的输入和输出参数。
type uppercaseRequest struct {
S string `json:"s"`
}
type uppercaseResponse struct {
V string `json:"v"`
Err string `json:"err,omitempty"` // errors don't JSON-marshal, so we use a string
}
type countRequest struct {
S string `json:"s"`
}
type countResponse struct {
V int `json:"v"`
}
Endpoints
gokit通过一个叫做Endpoints的抽象去提供大部分的功能。
一个endpoint张的像下面的这个样子
type Endpoint func(ctx context.Context, request interface{}) (response interface{}, err error)
他代表了一个RPC远程方法调用,也就是说,他实际上就是我们的service接口。现在我们将写一个简单的适配器把我们service的所有方法转换为endpoint。没一个适配器都带有一个StringService方法,并且返回一个与方法对应的endpoint。
import (
"context"
"github.com/go-kit/kit/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, err.Error()}, nil
}
return uppercaseResponse{v, ""}, 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}, nil
}
}
Transports
现在我们需要把写好的RPC方法暴露给外面的世界,这样它才能被其他程序调用。你可能对服务之间如何通讯有自己的看法,或许你再用thrift或许是通过http+json方式传递。不用担心的是gokit支持很多种传输。但是在这个最小化的例子中,我们使用http+json的方式实现。gokit提供了一个帮助类在transport/http包中。
import (
"context"
"encoding/json"
"log"
"net/http"
httptransport "github.com/go-kit/kit/transport/http"
)
func main() {
svc := stringService{}
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))
}
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)
}
完成
结束,完整代码在这里 ,运行之后可以通过curl命令验证。
$ curl -XPOST -d'{"s":"hello, world"}' localhost:8080/uppercase
{"v":"HELLO, WORLD","err":null}
$ curl -XPOST -d'{"s":"hello, world"}' localhost:8080/count
{"v":12}
总结
gokit声称自己是一个工具集而不是一个平台,所以我们可以专注到业务逻辑中去。本篇设计了两个概念endpoint transport service,service是我们业务逻辑的具体实现步骤,然后把service暴露出去成为一个endpoint 这个endpoint的传输需要依赖transpot。