1. 程式人生 > >Go - Kit 筆記 - 02 - Transport-http

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來學習本篇。看完原理再看他那個示例難度不大。