Go - Kit 筆記 - 02 - Transport-http
阿新 • • 發佈:2019-01-04
Transport – 傳輸層
位於github.com/go-kit/kit/transport/
,go-kit目前支援grpc、http、httprp、nats、netrpc、thrift
,傳輸層的作用是封裝端點。使端點可以被不同的傳輸協議呼叫。
Server結構
Server結構的作用是把端點封裝成http.Handler
,http.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
來學習本篇。看完原理再看他那個示例難度不大。