kit框架详解(基于go-kit)

kit是一个能够自动生成基于go-kit组件的框架,可以帮助我们快速创建微服务,而让我们只关注自己的业务逻辑和中间件实现。
仓库地址 github.com/kujtimiihoxha/kit
1 生成命令

// 1. 创建一个service模板 kit new service hello //kit n s hello ------ // 2. 编辑hello/pkg/service/service.go添加service type HelloService interface { // Add your methods here // e.x: Foo(ctx context.Context,s string)(rs string, err error) Foo(ctx context.Context, s string) (rs string, err error) Test(ctx context.Context, s string, i int64) (string, error) } // 3. 创建transport、endpoint、service层 kit g s hello --dmw //生成service目录结构 hello ├── cmd │ ├── service │ │├── service.go │ │└── service_gen.go │ └── main.go └── pkg ├── endpoint //endpoint layer │├── endpoint.go │├── endpoint_gen.go │└── middleware.go ├── http //transport layer │├── handler.go │└── handler_gen.go └── service //service layer ├── middleware.go └── service.go // 4. 生成client library kit g c hello // 生成client目录结构 hello └── client └── http └── http.go//client library

2 代码解释(http) 主要讲讲kit生成的transport层、endpoint层、service层分别是怎么实现的,又是怎么协同工作的。最后解释下service/middleware endpoint/middleware的工作原理。
go-kit的调用关系:
client访问微服务http server;经过transport层是decode获得http包request数据;然后交个endpoint处理,endpoint在go-kit是一个重要的概念,代表了一个可执行的服务,表现形式是一个函数type Endpoint func(ctx context.Context, request interface{}) (response interface{}, err error);endpoint将request传给业务函数处理,返回response,最后在一层层返回给client。
这里顺便解释下middleware,endpoint的middleware会在endpoint调用业务函数之前处理,service的middleware由service逻辑自己决定。
client server decode encode call return host:port/request response client transport endpoint service 下面的解释基于上面服务里面的两个业务:Foo/Test
transport层
http通过ListenAndServer监听连接,连接产生时转交给http.Handler处理,transport的核心就是将endpoint、decodeReq、encodeResp告诉http.Handler
比如下面的代码,处理路由为/foo的业务,"github.com/go-kit/kit/transport/http".NewServer创建一个server,里面包含一个http.Handler的实现。
// makeFooHandler creates the handler logic func makeFooHandler(m *http.ServeMux, endpoints endpoint.Endpoints, options []http1.ServerOption) { m.Handle("/foo", http1.NewServer(endpoints.FooEndpoint, decodeFooRequest, encodeFooResponse, options...)) }

http.Handler的实现
在文件/go-kit/kit/transport/http/server.go 中的ServeHTTP(w http.ResponseWriter, r *http.Request),核心代码为:
// Server wraps an endpoint and implements http.Handler. type Server struct { eendpoint.Endpoint decDecodeRequestFunc encEncodeResponseFunc before[]RequestFunc after[]ServerResponseFunc errorEncoder ErrorEncoder finalizer[]ServerFinalizerFunc loggerlog.Logger }// ServeHTTP implements http.Handler. func (s Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { ctx := r.Context() //request.Context request, err := s.dec(ctx, r) //decode request response, err := s.e(ctx, request) //endpoint function call s.enc(ctx, w, response) //encode response }

endpoint层
endpoint本质是一个处理函数类型,代表了一个RPC方法,每个服务需要针对服务进行具体实现
type Endpoint func(ctx context.Context, request interface{}) (response interface{}, err error),下面是一个实现的例子:
func MakeFooEndpoint(s service.HelloService) endpoint.Endpoint { return func(ctx context.Context, request interface{}) (interface{}, error) { req := request.(FooRequest) rs, err := s.Foo(ctx, req.S) return FooResponse{ Err: err, Rs:rs, }, nil } }

然后通过传入service创建endpoint:
func New(s service.HelloService) Endpoints { eps := Endpoints{ FooEndpoint:MakeFooEndpoint(s), TestEndpoint: MakeTestEndpoint(s), } return eps }

然后传入一个service创建一个endpoint实例:
svc := service.New() // service 部分会介绍 eps := endpoint.New(svc)

最后将endpoint实例传给上面的"github.com/go-kit/kit/transport/http".NewServer(),创建一个http.Handler()实现。
service层
service.go应该是go-kit里面最好理解的一层,也是定义业务最纯粹的地方,所有的业务定义在RPC接口HelloService中。
type HelloService interface { Foo(ctx context.Context, s string) (rs string, err error) Test(ctx context.Context, s string, i int64) (string, error) }

实现接口
type basicHelloService struct{}func (b *basicHelloService) Foo(ctx context.Context, s string) (rs string, err error) { return rs, err } func (b *basicHelloService) Test(ctx context.Context, s string, i int64) (s0 string, e1 error) { return s0, e1 }

创建service
func New() HelloService { return &basicHelloService{} }

启动一个微服务的详细流程
import http1 "github.com/go-kit/kit/transport/http"// 1. 创建一个service,此service为当前目录下service包 var svc HelloService = service.New()// 2. 创建一个Endpoints,此endpoint为当前目录下的endpoint包 eps := endpoint.New(svc) // 3. 创建一个microserver //3.1 创建http.Handler //这里省略(decodeFooRequest和encodeFooResponse实现) // type DecodeRequestFunc func(context.Context, *http.Request) (request interface{}, err error) // type EncodeResponseFunc func(context.Context, http.ResponseWriter, interface{}) error serv := http1.NewServer(eps.FooEndpoint, decodeFooRequest, encodeFooResponse, options...) //3.2 创建Mux server mux := http.NewServeMux() mux.Handle("/foo", serv) //3.3 启动微服务server http.ListenAndServe("localhost:8080", mux)

3 middleware(service/endpoint) middleware模块应该是go-kit最强大的地方,middleware通常是一个chain处理,比如go-kit中对于endpoint层的Middleware的定义endpoint.Middleware => type Middleware func(Endpoint) Endpoint。endpoint的middleware通常是对request做一些预处理,比如对header中的Authorization的处理。
对于service层的Middleware的定义需要根据service本身的业务接口定义,比如HelloService的Middleware可以定义为type Middleware func(HelloService) HelloService。go-kit提供了一些供service用的middleware组件。
通常service Middleware的实现是用对基类HelloService的扩展来实现的,比如
type loggingMiddleware struct { logger log.Logger nextHelloService }

这样就可以传入一个Base HelloService而返回一个loggingMiddleware,比如func(HelloService) loggingMiddleware
对于transport层没有明确是说明是middleware,但是可以添加option,比如记录log,trace等。
含有middleware的endpoint和service是go-kit的服务的标准创建方式,也就是说上面的介绍为了理解简化了创建流程,接下来将详细介绍含有middleware的服务创建。
包含middleware的服务创建流程
由于middleware的创建有些绕,这里先给出一个具体的service的创建的主要流程,将忽略具体的业务的实现,下面的介绍将围绕该流程展开,微服务包含两个方法Foo Test
启动一个带middleware微服务的详细流程
import http1 "github.com/go-kit/kit/transport/http"// 1. 创建一个Service Middleware类型定义 type Middleware func(HelloService) HelloService// 2. 扩展一个基于HelloService的middleware,并重写HelloService的所有RPC接口 type loggingMiddleware struct { logger log.Logger nextHelloService } func (l loggingMiddleware) Foo(ctx context.Context, s string) (rs string, err error) { // something return l.next.Foo(ctx, s) } func (l loggingMiddleware) Test(ctx context.Context, s string, i int64) (s0 string, e1 error) { // somethin return l.next.Test(ctx, s, i) }// 3. 实例化一个Service Middleware,并append到一个[]Middleware var logger log.Logger logMW := func(next HelloService) HelloService { return &loggingMiddleware{logger, next} }// 4. 创建一个service,这里需要重新定义service.New()为service.New(mws []Middleware),传入的为需要处理的middleware,后面会具体介绍这些middleware是怎么串起来的形成一个service chain的。 mws := []Middleware{logMW} var svc HelloService = service.New(mws)// 5. 实例化一个endpoint.Middleware => type Middleware func(Endpoint) Endpoint logeMW := func(next endpoint.Endpoint) endpoint.Endpoint { return func(ctx context.Context, request interface{}) (response interface{}, err error) { // something TODO about log // ctx = ... // request = ... logger.Log("...") return next(ctx, request) } }// 6. 创建一个Endpoint,这里需要重新定义`endpoint.New(service.HelloService)`为`endpoint.New(service.HelloService,map[string][]endpoint.Middleware)`,传入一个`map[string][]endpoint.Middleware{}`。emws := map[string][]endpoint.Middleware{} emws["Foo"] = []endpoint.Middleware{logeMW} // 这里的"Foo"是HelloService里面定义的RPC方法,也可以更多的类似logeMW的实现; //如果"Test"也有endpoint.Middleware,则也应该添加 // emws["Test"] = []endpoint.Middleware{logeMW} eps := endpoint.New(svc,emws)// eps为Endpoints类型,即所有Service服务接口对应的endpoint.Endpoint集合: // type Endpoints struct { //FooEndpointendpoint.Endpoint //TestEndpoint endpoint.Endpoint // }// 7. 创建一个microserver //7.1 创建http.Handler //这里省略(decodeFooRequest和encodeFooResponse实现) // type DecodeRequestFunc func(context.Context, *http.Request) (request interface{}, err error) // type EncodeResponseFunc func(context.Context, http.ResponseWriter, interface{}) error servfoo := http1.NewServer(eps.FooEndpoint, decodeFooRequest, encodeFooResponse, options...) servtest := http1.NewServer(eps.TestEndpoint, decodeFooRequest, encodeFooResponse, options...) //7.2 创建Mux server mux := http.NewServeMux() mux.Handle("/foo", servfoo) mux.Handle("/test", servtest) //7.3 启动微服务server http.ListenAndServe("localhost:8080", mux)

endpoint/middleware
endpoint.Endpoint定义:
// Endpoint is the fundamental building block of servers and clients. // It represents a single RPC method. type Endpoint func(ctx context.Context, request interface{}) (response interface{}, err error)

endpoint.Middleware定义:
// Middleware is a chainable behavior modifier for endpoints. type Middleware func(Endpoint) Endpoint

所以Middleware的实现方式可以理解为是一个输入输出链模型:
http/req ctx,req ctx1,req1...ctxN,reqN 客户端请求 serverHTTP ctx1,req1 response 其实kit对endpoint的实现是比较巧妙的,具体体现在endpoint.Newendpoint.Middleware,看下他们的实现:
// New returns a Endpoints struct that wraps the provided service, and wires in all of the // expected endpoint middlewares func New(s service.HelloService, mdw map[string][]endpoint.Middleware) Endpoints { eps := Endpoints{ FooEndpoint:MakeFooEndpoint(s), TestEndpoint: MakeTestEndpoint(s), } for _, m := range mdw["Foo"] { eps.FooEndpoint = m(eps.FooEndpoint) } for _, m := range mdw["Test"] { eps.TestEndpoint = m(eps.TestEndpoint) } return eps }

func(next endpoint.Endpoint) endpoint.Endpoint { return func(ctx context.Context, request interface{}) response interface{}, err error) { // something need to do return next(ctx, request) } }

来分析下核心代码:
第一段代码中的: for _, m := range mdw["Foo"] { eps.FooEndpoint = m(eps.FooEndpoint) } //-------- 第二段代码中的: return next(ctx, request)

也就是说在New创建的时候,会创建一个endpoint.Endpoint的链式调用,最后创建的将会先执行。当收到http的请求时,http.Handler会调用endpoint.Endpoint(ctx,req),从而依次往后调,最后将调用业务接口的Endpoint。
service/middleware
service的middleware和endpoint的middleware是类似的,也是一个链式调用,先来看几个概念:
Middleware的定义:
// Middleware describes a service middleware. type Middleware func(HelloService) HelloService

具体某个Middleware的定义:
type loggingMiddleware struct { logger log.Logger nextHelloService }

我们来看看其中实现的奥秘,共三段代码:
loggingMiddleware包含的HelloService接口的实现:
func (l loggingMiddleware) Foo(ctx context.Context, s string) (rs string, err error) { defer func() { l.logger.Log("method", "Foo", "s", s, "rs", rs, "err", err) }() return l.next.Foo(ctx, s) } func (l loggingMiddleware) Test(ctx context.Context, s string, i int64) (s0 string, e1 error) { defer func() { l.logger.Log("method", "Test", "s", s, "i", i, "s0", s0, "e1", e1) }() return l.next.Test(ctx, s, i) }

Middleware的实现:
var logger log.Logger func(next HelloService) HelloService { return &loggingMiddleware{logger, next} }

创建一个service的时候
// New returns a HelloService with all of the expected middleware wired in. func New(middleware []Middleware) HelloService { var svc HelloService = NewBasicHelloService() for _, m := range middleware { svc = m(svc) } return svc }

来分析下核心代码:
第一段代码中的: return l.next.Foo(ctx, s) //-------- 第二段代码中的: return &loggingMiddleware{logger, next} //-------- 第三段代码中的: for _, m := range middleware { svc = m(svc) }

【kit框架详解(基于go-kit)】也就是说每个Middleware会接收一个老的HelloService返回一个包含老的HelloService的新的HelloService,同时新的HelloService的接口实现会调用老的HelloService的接口return l.next.Foo(ctx, s)
在创建的时候,会依次调用,从而将这些Middleware串起来。
4 Client kit g c hello
client的连接具体参考生成client库。
package mainimport ( "context" "fmt" client "hello/client/http" "github.com/go-kit/kit/transport/http" )func main() { svc, err := client.New("http://localhost:8081", map[string][]http.ClientOption{}) if err != nil { panic(err) } r, err := svc.Foo(context.Background(), "hello") if err != nil { fmt.Println("Error:", err) return } fmt.Println("Result:", r) }

5 多协议支持 go-kit 除了支持http还支持grpc,amqp,nats,thrift。比如,grpc使用kit g s hello -t grpc,nats使用kit g s hello -t nats
这些的实现框架与http是一致的。

    推荐阅读