Skip to content

Commit

Permalink
feat NewHttpRouter (#282)
Browse files Browse the repository at this point in the history
* add NewHttpRouter to support init httprouter and using middleware

* revert unexpected change

* del redundant fn call

* fix discussion

* fix discussion

* rm MiddlewareQueue, mv middlewares to HttpRouter struct

* rm r.Use in NewHttpRouter

Co-authored-by: zhanglin7316 <[email protected]>
  • Loading branch information
xyzzhang and zhanglin7316 authored Aug 12, 2021
1 parent 6d24a25 commit bcf9df8
Show file tree
Hide file tree
Showing 4 changed files with 205 additions and 8 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ require (
github.com/coreos/etcd v3.3.22+incompatible
github.com/gin-gonic/gin v1.7.2
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0
github.com/julienschmidt/httprouter v1.2.0
github.com/julienschmidt/httprouter v1.3.1-0.20200921135023-fe77dd05ab5a
github.com/opentracing/opentracing-go v1.1.0
github.com/shawnfeng/consistent v1.0.3
github.com/stretchr/testify v1.7.0
Expand Down
10 changes: 4 additions & 6 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,10 @@ github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfV
github.com/julienschmidt/httprouter v1.0.1-0.20150106073633-b55664b9e920/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/julienschmidt/httprouter v1.2.0 h1:TDTW5Yz1mjftljbcKqRcrYhd4XeOoI98t+9HbQbYf7g=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U=
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
github.com/julienschmidt/httprouter v1.3.1-0.20200921135023-fe77dd05ab5a h1:VTF3sHLbpm2PdWMPKVWUMwKg85VE7Ep7wgBw8ETYri8=
github.com/julienschmidt/httprouter v1.3.1-0.20200921135023-fe77dd05ab5a/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
github.com/kaneshin/go-pkg v0.0.0-20150919125626-a8e1479186cf h1:kBpksZbhb1xNFDTPbxh5H5G0o0MJZKrH004ELvq/zWI=
github.com/kaneshin/go-pkg v0.0.0-20150919125626-a8e1479186cf/go.mod h1:XI2QuPvM6xUXWcpwlqaMB+SxxV7XNr3aO/DrUK8lRZo=
Expand Down Expand Up @@ -728,12 +732,6 @@ gitlab.pri.ibanyu.com/middleware/delayqueue v0.0.0-20200213090847-cd24af2bd1f2 h
gitlab.pri.ibanyu.com/middleware/delayqueue v0.0.0-20200213090847-cd24af2bd1f2/go.mod h1:4nx2iPOcfEy+4QbgoNq+ZuqwV0+ZvaF3dyvM34uObFo=
gitlab.pri.ibanyu.com/middleware/dolphin v1.0.6 h1:AzQfX786Jegn7LueD9QSxX4EyBCy5jOJVTT2byB8PcU=
gitlab.pri.ibanyu.com/middleware/dolphin v1.0.6/go.mod h1:205+jmF+Ai0JHm/SzJP1Bpkrd64H98XO+Q99s0wvyuA=
gitlab.pri.ibanyu.com/middleware/dolphin v1.0.7 h1:4d7ZR756tK+T35ETGLBhgkfsYzLK52Y4v7M2N3+DIeI=
gitlab.pri.ibanyu.com/middleware/dolphin v1.0.7/go.mod h1:205+jmF+Ai0JHm/SzJP1Bpkrd64H98XO+Q99s0wvyuA=
gitlab.pri.ibanyu.com/middleware/dolphin v1.0.8-0.20210701024449-d2244571584d h1:vdTO/E+sDshYoUD4bR0sWpSCzEKO/sERS8Ur5edmvQI=
gitlab.pri.ibanyu.com/middleware/dolphin v1.0.8-0.20210701024449-d2244571584d/go.mod h1:vPp90q09zL2drW2rljdrkClbcWysYoHGjkp70+xKeXw=
gitlab.pri.ibanyu.com/middleware/dolphin v1.0.8 h1:U+RIXlX3pLfRW9cFw5yqzdWTKVoQrbPD5/yWK/cDXII=
gitlab.pri.ibanyu.com/middleware/dolphin v1.0.8/go.mod h1:vPp90q09zL2drW2rljdrkClbcWysYoHGjkp70+xKeXw=
gitlab.pri.ibanyu.com/middleware/dolphin v1.0.9 h1:R7Uz8EAt9XFzpj/X0EzBznYc8lnxNKfjvKooTT5bTZ4=
gitlab.pri.ibanyu.com/middleware/dolphin v1.0.9/go.mod h1:vPp90q09zL2drW2rljdrkClbcWysYoHGjkp70+xKeXw=
gitlab.pri.ibanyu.com/middleware/seaweed v1.0.5/go.mod h1:iCFyLPlxZOk9Z6sJ+b92gN+mN1OsmZ67Ez8j/1wtp14=
Expand Down
32 changes: 31 additions & 1 deletion util/service/power.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,11 +148,41 @@ func (dr *driverBuilder) powerProcessorDriver(ctx context.Context, n string, p P
}
return servInfo, nil

case *HttpRouter:
sa, err := powerHttpRouter(addr, d.Router)
if err != nil {
return nil, err
}
servInfo := &ServInfo{
Type: PROCESSOR_HTTP,
Addr: sa,
}
return servInfo, nil

default:
return nil, fmt.Errorf("processor: %s driver not recognition", n)
}
}

func powerHttpRouter(addr string, router *httprouter.Router) (string, error) {
fun := "powerHttpRouter -->"
ctx := context.Background()

netListen, laddr, err := listenServAddr(ctx, addr)
if err != nil {
return "", err
}

go func() {
err := http.Serve(netListen, router)
if err != nil {
xlog.Panicf(ctx, "%s laddr[%s]", fun, laddr)
}
}()

return laddr, nil
}

func powerHttp(addr string, router *httprouter.Router, middlewares ...middleware) (string, error) {
fun := "powerHttp -->"
ctx := context.Background()
Expand Down Expand Up @@ -343,7 +373,7 @@ func reloadRouter(processor string, server interface{}, driver interface{}) erro
func metricMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
ctx = contextWithErrCode(ctx,1)
ctx = contextWithErrCode(ctx, 1)
newR := r.WithContext(ctx)
path := r.URL.Path
path = ParseUriApi(path)
Expand Down
169 changes: 169 additions & 0 deletions util/service/server_http.go
Original file line number Diff line number Diff line change
Expand Up @@ -577,3 +577,172 @@ func extractContextHeadFromReqHeader(ctx context.Context, req *http.Request) con
ctx = context.WithValue(ctx, xcontext.ContextKeyHead, head)
return ctx
}

type Middleware func(httprouter.Handle) httprouter.Handle

type HttpRouter struct {
*httprouter.Router
middlewares []Middleware
}

// NewHttpRouter returns a new initialized Router.
// Supported trace, metric and custom middlewares
func NewHttpRouter() *HttpRouter {
fun := "NewHttpRouter -->"

router := httprouter.New()
router.SaveMatchedRoutePath = true

middlewares := []Middleware{TraceForHttpRouter(), MetricForHttpRouter()}

disableContextCancel := isDisableContextCancel()
if disableContextCancel {
middlewares = append(middlewares, DisableContextCancelForHttpRouter())
}
xlog.Infof(context.TODO(), "%s disableContextCancel: %v", fun, disableContextCancel)

return &HttpRouter{
Router: router,
middlewares: middlewares,
}
}

// Use attaches global middlewares to the router
func (r *HttpRouter) Use(middlewares ...Middleware) {
r.middlewares = append(r.middlewares, middlewares...)
}

func (r *HttpRouter) wrap(fn httprouter.Handle) httprouter.Handle {
if len(r.middlewares) == 0 {
return fn
}

// There is at least one item in the middleware list.
result := r.middlewares[0](fn)

for _, m := range r.middlewares {
result = m(result)
}

return result
}

// GET is a shortcut for router.Handle(http.MethodGet, path, handle)
func (r *HttpRouter) GET(path string, handle httprouter.Handle) {
r.Handle(http.MethodGet, path, handle)
}

// HEAD is a shortcut for router.Handle(http.MethodHead, path, handle)
func (r *HttpRouter) HEAD(path string, handle httprouter.Handle) {
r.Handle(http.MethodHead, path, handle)
}

// OPTIONS is a shortcut for router.Handle(http.MethodOptions, path, handle)
func (r *HttpRouter) OPTIONS(path string, handle httprouter.Handle) {
r.Handle(http.MethodOptions, path, handle)
}

// POST is a shortcut for router.Handle(http.MethodPost, path, handle)
func (r *HttpRouter) POST(path string, handle httprouter.Handle) {
r.Handle(http.MethodPost, path, handle)
}

// PUT is a shortcut for router.Handle(http.MethodPut, path, handle)
func (r *HttpRouter) PUT(path string, handle httprouter.Handle) {
r.Handle(http.MethodPut, path, handle)
}

// PATCH is a shortcut for router.Handle(http.MethodPatch, path, handle)
func (r *HttpRouter) PATCH(path string, handle httprouter.Handle) {
r.Handle(http.MethodPatch, path, handle)
}

// DELETE is a shortcut for router.Handle(http.MethodDelete, path, handle)
func (r *HttpRouter) DELETE(path string, handle httprouter.Handle) {
r.Handle(http.MethodDelete, path, handle)
}

// Handle registers a new request handle with the given path and method.
//
// For GET, POST, PUT, PATCH and DELETE requests the respective shortcut
// functions can be used.
//
// This function is intended for bulk loading and to allow the usage of less
// frequently used, non-standardized or custom methods (e.g. for internal
// communication with a proxy).
func (r *HttpRouter) Handle(method, path string, handle httprouter.Handle) {
r.Router.Handle(method, path, r.wrap(handle))
}

// Handler is an adapter which allows the usage of an http.Handler as a
// request handle.
// The Params are available in the request context under ParamsKey.
func (r *HttpRouter) Handler(method, path string, handler http.Handler) {
//r.Router.Handler()
r.Router.Handle(method, path, r.wrap(warpHttpHandlerToHttpRouterHandle(handler)))
}

// HandlerFunc is an adapter which allows the usage of an http.HandlerFunc as a
// request handle.
func (r *HttpRouter) HandlerFunc(method, path string, handler http.HandlerFunc) {
r.Handler(method, path, handler)
}

// warpHttpToHttpRouter wraps http.Handler and returns httprouter.Handle
func warpHttpHandlerToHttpRouterHandle(next http.Handler) httprouter.Handle {
return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
ctx := context.WithValue(r.Context(), httprouter.ParamsKey, ps)
//call next middleware with new context
next.ServeHTTP(w, r.WithContext(ctx))
}
}

func TraceForHttpRouter() Middleware {
return func(fn httprouter.Handle) httprouter.Handle {
return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
span := xtrace.SpanFromContext(r.Context())
if span == nil {
fullPath := ps.MatchedRoutePath()
newSpan, ctx := xtrace.StartSpanFromContext(r.Context(), fullPath)
r = r.WithContext(ctx)
span = newSpan
}
defer span.Finish()

if sc, ok := span.Context().(jaeger.SpanContext); ok {
w.Header()[HttpHeaderKeyTraceID] = []string{fmt.Sprint(sc.TraceID())}
}
fn(w, r, ps)
}
}
}

func MetricForHttpRouter() Middleware {
return func(fn httprouter.Handle) httprouter.Handle {
return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
ctx := r.Context()
ctx = contextWithErrCode(ctx, 1)
newR := r.WithContext(ctx)
path := ps.MatchedRoutePath()

now := time.Now()
fn(w, newR, ps)
dt := time.Since(now)

errCode := getErrCodeFromContext(ctx)

group, serviceName := GetGroupAndService()
_metricAPIRequestCount.With(xprom.LabelGroupName, group, xprom.LabelServiceName, serviceName, xprom.LabelAPI, path, xprom.LabelErrCode, strconv.Itoa(errCode)).Inc()
_metricAPIRequestTime.With(xprom.LabelGroupName, group, xprom.LabelServiceName, serviceName, xprom.LabelAPI, path, xprom.LabelErrCode, strconv.Itoa(errCode)).Observe(float64(dt / time.Millisecond))
}
}
}

func DisableContextCancelForHttpRouter() Middleware {
return func(fn httprouter.Handle) httprouter.Handle {
return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
newR := r.WithContext(xcontext.NewValueContext(r.Context()))
fn(w, newR, ps)
}
}
}

0 comments on commit bcf9df8

Please sign in to comment.