业务逻辑
创建接口
type StringService interface {Uppercase(string) (string, error)Count(string) int}
根据j接口实现业务逻辑
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 emptyvar ErrEmpty = errors.New("Empty string")
请求和返回
在Go kit中,主要的消息传递模式是RPC。因此,接口中的每个方法都将建模为远程方法调用。对于每个方法,我们都需要定义请求和响应体结构,以此表示请求和返回参数。
Endpoints
Go kit通过一个称为抽象的endpoint来提供其大部分功能。
端点的定义如下(你不必把它放在代码中的任何地方,它由go-kit提供):
type Endpoint func(ctx context.Context, request interface{}) (response interface{}, err error)
它代表一个RPC。也就是说,我们的服务接口中只有一个方法。我们将编写简单的适配器,将每个服务方法转换为一个端点。每个适配器接受一个StringService,并返回与其中一个方法对应的端点。
// Endpoints are a primary abstraction in go-kit. An endpoint represents a single RPC (method in our service interface)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
现在我们需要向外部公开您的服务,以便可以调用它。您的组织可能已经对服务应该如何相互通信有了自己的看法。也许您使用Thrift,或者通过HTTP定制JSON。Go kit支持许多现成的Transports。
对于这个最小的示例服务,让我们使用HTTP上的JSON。Go kit提供了一个helper结构,在package 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 uppercaseRequestif 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 countRequestif 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)}
运行程序
go run main.go
测试
➜ stringsvc1 git:(master) ✗ curl -XPOST -d'{"s":"hello, world"}' localhost:8080/uppercase{"v":"HELLO, WORLD"}➜ stringsvc1 git:(master) ✗ curl -XPOST -d'{"s":"hello, world"}' localhost:8080/count{"v":12}
Middlewares
如果没有完整的日志记录和检测工具,就不能认为服务是可用于生产的。
Transport logging
Any component that needs to log should treat the logger like a dependency, same as a database connection. So, we construct our logger in our func main, and pass it to components that need it. We never use a globally-scoped logger.
We could pass a logger directly into our stringService implementation, but there’s a better way. Let’s use a middleware, also known as a decorator. A middleware is a function that takes an endpoint and returns an endpoint.
任何需要记录日志的组件都应该将记录器视为依赖项,就像数据库连接一样。因此,我们在main函数中构造logger,并将它传递给需要它的组件。我们从不使用全局范围的记录器。
我们可以直接将记录器传递到stringService实现中,但是有一种更好的方法。让我们使用中间件,也称为装饰器。中间件是一个接受端点并返回端点的函数。
