Go - Kit 笔记 - 02 - Transport-http

Transport – 传输层

位于github.com/go-kit/kit/transport/,go-kit目前支持grpc、http、httprp、nats、netrpc、thrift,传输层的作用是封装端点。使端点可以被不同的传输协议调用。

Server结构

Server结构的作用是把端点封装成http.Handlerhttp.Handler位于net/http/server.go中,实则是一个接口,定义如下:

type Handler interface {
        ServeHTTP(ResponseWriter, *Request)
}
注:如果对```net/http```包不太了解,可以看一看《go-web编程》。

Server结构定义如下:

type ErrorEncoder func(ctx context.Context, err error, w http.ResponseWriter)
type DecodeRequestFunc func(context.Context, *http.Request) (request interface{}, err error)
type EncodeRequestFunc func(context.Context, *http.Request, interface{}) error

type Server struct {
        e            endpoint.Endpoint  //端点
        dec          DecodeRequestFunc  //解码函数,需要自定义
        enc          EncodeResponseFunc  //编码函数,需要自定义
        before       []RequestFunc //前置函数
        after        []ServerResponseFunc //后置函数
        errorEncoder ErrorEncoder //错误函数
        finalizer    []ServerFinalizerFunc //终结器函数
        logger       log.Logger //日志
}

NewServer开始跟代码:

type ServerOption func(*Server)
func NewServer(
        e endpoint.Endpoint, //e,不解释
        dec DecodeRequestFunc, //调用端点之前,会调用dec对数据进行解码
        enc EncodeResponseFunc, //调用端点之后,会调用enc对数据进行编码
        options ...ServerOption, //看函数体中的for循环。
) *Server {
        s := &Server{
                e:            e, 
                dec:          dec, 
                enc:          enc, 
                errorEncoder: DefaultErrorEncoder, //transport/http/server.go里面有定义,有兴趣的可以看一眼。
                logger:       log.NewNopLogger(),  //go-kit自己的log模块,有机会看。
        }
        for _, option := range options { //循环执行option。option是可以自定义的。
                option(s)
        }
        return s
}

重点来了,Server结构实现了ServerHTTP方法,所以可以当作http.Handler接口使用,然后可已调用http.HandFunc添加到http服务中。

func (s Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
        ctx := r.Context()  //获取上下文

        if len(s.finalizer) > 0 {   //如果终结器个数大于0,当函数退出后执行终结器(默认终结器个数为0的)
                iw := &interceptingWriter{w, http.StatusOK, 0}
                defer func() {          //ServerHTTP退出时,会执行里面相应的函数
                        ctx = context.WithValue(ctx, ContextKeyResponseHeaders, iw.Header())
                        ctx = context.WithValue(ctx, ContextKeyResponseSize, iw.written)
                        for _, f := range s.finalizer {
                                f(ctx, iw.code, r)
                        }
                }()
                w = iw
        }

        //执行befor中设置的函数
        for _, f := range s.before {
                ctx = f(ctx, r)
        }

        //执行解码函数
        request, err := s.dec(ctx, r)
        if err != nil {   //如果出错,记录日志,执行错误处理函数
                s.logger.Log("err", err)
                s.errorEncoder(ctx, err, w)
                return
        }

        //执行端点
        response, err := s.e(ctx, request)
        if err != nil {   //如果出错,记录日志,执行错误处理函数
                s.logger.Log("err", err)
                s.errorEncoder(ctx, err, w)
                return
        }

        //执行after函数
        for _, f := range s.after {
                ctx = f(ctx, w)
        }
        
        //执行编码函数
        if err := s.enc(ctx, w, response); err != nil {
                s.logger.Log("err", err)
                s.errorEncoder(ctx, err, w)
                return
        }

Client结构

可以理解为http爬虫,能发送各种http请求。

type HTTPClient interface {
        Do(req *http.Request) (*http.Response, error)
}
type Client struct {
        client         HTTPClient  //接口
        method         string   //方法(get、post、put、delete。。。)
        tgt            *url.URL   //网址
        enc            EncodeRequestFunc   //编码函数
        dec            DecodeResponseFunc  //解码函数
        before         []RequestFunc   //前置函数
        after          []ClientResponseFunc  //后置函数
        finalizer      []ClientFinalizerFunc  //终结器函数
        bufferedStream bool //是否开启缓冲
}
func NewClient(
        method string,  //设置方法
        tgt *url.URL,   //网址
        enc EncodeRequestFunc,   //编码函数
        dec DecodeResponseFunc,   //解码函数
        options ...ClientOption,  //选项函数
) *Client {
        c := &Client{
                client:         http.DefaultClient,   //net/http包中定义的,直接拿来用了
                method:         method,
                tgt:            tgt,
                enc:            enc,
                dec:            dec,
                before:         []RequestFunc{},
                after:          []ClientResponseFunc{},
                bufferedStream: false,
        }
        for _, option := range options {
                option(c)
        }
        return c
}

重点看下下面这个函数:

func (c Client) Endpoint() endpoint.Endpoint {   //此函数返回一个端点。
        return func(ctx context.Context, request interface{}) (interface{}, error) {
                ctx, cancel := context.WithCancel(ctx)

                var (
                        resp *http.Response
                        err  error
                )
                if c.finalizer != nil {   //终结器
                        defer func() {
                                if resp != nil {
                                        ctx = context.WithValue(ctx, ContextKeyResponseHeaders, resp.Header)
                                        ctx = context.WithValue(ctx, ContextKeyResponseSize, resp.ContentLength)
                                }
                                for _, f := range c.finalizer {
                                        f(ctx, err)
                                }
                        }()
                }

			//新建一个http请求对象
                req, err := http.NewRequest(c.method, c.tgt.String(), nil)
                if err != nil {   //如果新建失败,则切出上下文
                        cancel()
                        return nil, err
                }

				//解码,如果出错切出上下文
                if err = c.enc(ctx, req, request); err != nil {
                        cancel()
                        return nil, err
                }

				//执行befor
                for _, f := range c.before {
                        ctx = f(ctx, req)
                }

			   //发送请求
                resp, err = c.client.Do(req.WithContext(ctx))

                if err != nil {
                        cancel()
                        return nil, err
                }

                //是否开启缓冲
                if c.bufferedStream {
                        resp.Body = bodyWithCancel{ReadCloser: resp.Body, cancel: cancel}
                } else {
                        defer resp.Body.Close()
                        defer cancel()
                }

				//执行after函数
                for _, f := range c.after {
                        ctx = f(ctx, resp)
                }

				//编码
                response, err := c.dec(ctx, resp)
                if err != nil {
                        return nil, err
                }

				//返回
                return response, nil
        }
}

总结

只能说简单看了下代码吧。最重要的还是里面的执行顺序。
server:
1、befor函数
2、解码
3、端点
4、after函数
5、编码
6、finalizer
client:
1、解码
2、befor函数
3、发请求
4、after函数
5、编码

注1:可以结合kit/examples/stringserver1来学习本篇。看完原理再看他那个示例难度不大。

你可能感兴趣的:(Go - Kit 笔记 - 02 - Transport-http)