transporter/http 中基于 gorilla/mux HTTP路由框架實現了?Transporter
?,用以注冊 http 到 ?kratos.Server()
? 中。
Network(network string) ServerOption
?配置服務端的 network 協議,如 tcp
Address(addr string) ServerOption
?配置服務端監(jiān)聽的地址
Timeout(timeout time.Duration) ServerOption
?配置服務端的超時設置
Logger(logger log.Logger) ServerOption
?配置服務端使用日志
Middleware(m ...middleware.Middleware) ServerOption
?配置服務端的 kratos Service中間件
Filter(filters ...FilterFunc) ServerOption
?配置服務端的 kratos 全局HTTP原生Fitler,此Filter執(zhí)行順序在Service中間件之前
RequestDecoder(dec DecodeRequestFunc) ServerOption
?配置kratos服務端的 HTTP Request Decode方法,用來將Request Body解析至用戶定義的pb結構體中 我們看下kratos中默認的RequestDecoder是怎么實現的:
func DefaultRequestDecoder(r *http.Request, v interface{}) error {
// 從Request Header的Content-Type中提取出對應的解碼器
codec, ok := CodecForRequest(r, "Content-Type")
// 如果找不到對應的解碼器此時會報錯
if !ok {
return errors.BadRequest("CODEC", r.Header.Get("Content-Type"))
}
data, err := ioutil.ReadAll(r.Body)
if err != nil {
return errors.BadRequest("CODEC", err.Error())
}
if err = codec.Unmarshal(data, v); err != nil {
return errors.BadRequest("CODEC", err.Error())
}
return nil
}
那么如果我們想要擴展或者替換Content-Type對應的解析實現,就可以通過http.RequestDecoder()來替換kratos默認的RequestDecoder, 或者也可以通過在encoding中注冊或覆蓋一個Content-Type對應的codec來進行擴展
ResponseEncoder(en EncodeResponseFunc) ServerOption
?配置kratos服務端的 HTTP Response Encode方法,用來將用戶pb定義里的reply結構體序列化后寫入Response Body中 我們看下kratos中默認的ResponseEncoder是怎么實現的:
func DefaultResponseEncoder(w http.ResponseWriter, r *http.Request, v interface{}) error {
// 通過Request Header的Accept中提取出對應的編碼器
// 如果找不到則忽略報錯,并使用默認json編碼器
codec, _ := CodecForRequest(r, "Accept")
data, err := codec.Marshal(v)
if err != nil {
return err
}
// 在Response Header中寫入編碼器的scheme
w.Header().Set("Content-Type", httputil.ContentType(codec.Name()))
w.Write(data)
return nil
}
那么如果我們想要擴展或者替換Accept對應的序列化實現,就可以通過http.ResponseEncoder()來替換kratos默認的ResponseEncoder, 或者也可以通過在encoding中注冊或覆蓋一個Accept對應的codec來進行擴展
ErrorEncoder(en EncodeErrorFunc) ServerOption
?配置kratos服務端的 HTTP Error Encode方法,用來將業(yè)務拋出的error序列化后寫入Response Body中,并設置HTTP Status Code 我們看下kratos中默認的ErrorEncoder是怎么實現的:
func DefaultErrorEncoder(w http.ResponseWriter, r *http.Request, err error) {
// 拿到error并轉換成kratos Error實體
se := errors.FromError(err)
// 通過Request Header的Accept中提取出對應的編碼器
codec, _ := CodecForRequest(r, "Accept")
body, err := codec.Marshal(se)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", httputil.ContentType(codec.Name()))
// 設置HTTP Status Code
w.WriteHeader(int(se.Code))
w.Write(body)
}
??NewServer(opts ...ServerOption) *Server
?
?
hs := http.NewServer()
app := kratos.New(
kratos.Name("kratos"),
kratos.Version("v1.0.0"),
kratos.Server(hs),
)
hs := http.NewServer(
http.Address(":8000"),
http.Middleware(
logging.Server(),
),
)
if tr, ok := transport.FromServerContext(ctx); ok {
kind = tr.Kind().String()
operation = tr.Operation()
// 斷言成HTTP的Transport可以拿到特殊信息
if ht,ok := tr.(*http.Tranport);ok{
fmt.Println(ht.Request())
}
}
func (s *Server) Route(prefix string, filters ...FilterFunc) *Router
?創(chuàng)建一個新的HTTP Server Router,同時可以傳遞kratos的HTTP Filter攔截器 我們看下用法:
r := s.Route("/v1")
r.GET("/helloworld/{name}", _Greeter_SayHello0_HTTP_Handler(srv))
func (s *Server) Handle(path string, h http.Handler)
?將path添加到路由中,并使用標準的HTTP Handler來處理
func (s *Server) HandlePrefix(prefix string, h http.Handler)
?前綴匹配的方式將prefix添加到路由中,并使用標準的HTTP Handler來處理
func (s *Server) ServeHTTP(res http.ResponseWriter, req *http.Request)
?實現了標準庫的HTTP Handler接口
其他路由使用方法參考: https://github.com/go-kratos/examples/tree/main/http/middlewares
在Kratos HTTP中使用gin框架: https://github.com/go-kratos/kratos/blob/main/examples/http/gin/main.go
WithTransport(trans http.RoundTripper) ClientOption
?配置客戶端的HTTP RoundTripper
WithTimeout(d time.Duration) ClientOption
?配置客戶端的請求默認超時時間,如果有鏈路超時優(yōu)先使用鏈路超時時間
WithUserAgent(ua string) ClientOption
?配置客戶端的默認User-Agent
WithMiddleware(m ...middleware.Middleware) ClientOption
?配置客戶端使用的 kratos client中間件
WithEndpoint(endpoint string) ClientOption
?配置客戶端使用的對端連接地址,如果不使用服務發(fā)現則為ip:port,如果使用服務發(fā)現則格式為discovery://\<authority>/\<serviceName>,這里\<authority>可以默認填空
WithDiscovery(d registry.Discovery) ClientOption
?配置客戶端使用的服務發(fā)現
WithRequestEncoder(encoder EncodeRequestFunc) ClientOption
?配置客戶端的 HTTP Request Encode方法,用來將戶定義的pb結構體中序列化至Request Body 我們看下默認的encoder:
func DefaultRequestEncoder(ctx context.Context, contentType string, in interface{}) ([]byte, error) {
// 通過外部配置的contentType獲取encoder類型
name := httputil.ContentSubtype(contentType)
// 拿到實際的encoder
body, err := encoding.GetCodec(name).Marshal(in)
if err != nil {
return nil, err
}
return body, err
}
WithResponseDecoder(decoder DecodeResponseFunc) ClientOption
?配置客戶端的 HTTP Response Decode方法,用來將Response Body解析至用戶定義的pb結構體中 我們看下kratos中默認的decoder是怎么實現的:
func DefaultResponseDecoder(ctx context.Context, res *http.Response, v interface{}) error {
defer res.Body.Close()
data, err := ioutil.ReadAll(res.Body)
if err != nil {
return err
}
// 這里根據Response Header中的Content-Type拿到對應的decoder
// 然后進行Unmarshal
return CodecForResponse(res).Unmarshal(data, v)
}
WithErrorDecoder(errorDecoder DecodeErrorFunc) ClientOption
?配置客戶端的Error解析方法 我們看下kratos中默認的error decoder是怎么實現的:
func DefaultErrorDecoder(ctx context.Context, res *http.Response) error {
// HTTP Status Code 為最高優(yōu)先級
if res.StatusCode >= 200 && res.StatusCode <= 299 {
return nil
}
defer res.Body.Close()
data, err := ioutil.ReadAll(res.Body)
if err == nil {
e := new(errors.Error)
// 這里根據Response Header中的Content-Type拿到對應的response decoder
// 然后解析出error主體內容
if err = CodecForResponse(res).Unmarshal(data, e); err == nil {
// HTTP Status Code 為最高優(yōu)先級
e.Code = int32(res.StatusCode)
return e
}
}
// 如果沒有返回合法的Response Body則直接以HTTP Status Code為準
return errors.Errorf(res.StatusCode, errors.UnknownReason, err.Error())
}
WithBalancer(b balancer.Balancer) ClientOption
?配置客戶端的負載均衡策略
WithBlock() ClientOption
?配置客戶端的Dial策略為阻塞(直到服務發(fā)現發(fā)現節(jié)點才返回),默認為異步非阻塞
conn, err := http.NewClient(
context.Background(),
http.WithEndpoint("127.0.0.1:8000"),
)
conn, err := http.NewClient(
context.Background(),
http.WithEndpoint("127.0.0.1:9000"),
http.WithMiddleware(
recovery.Recovery(),
),
)
conn, err := http.NewClient(
context.Background(),
http.WithEndpoint("discovery:///helloworld"),
http.WithDiscovery(r),
)
更多建議: