加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 大数据 > 正文

go-restful实战与深入分析之源码篇

发布时间:2020-12-16 18:15:25 所属栏目:大数据 来源:网络整理
导读:上一篇分析了go 原生的http服务。下面开始介绍go restful源码分析,有了上一篇的铺垫这篇文章讲解起来就很简单了。和go 的http一样启动监听: wsContainer := restful.NewContainer() server := http.Server{Addr : ":8080" ,Handler: wsContainer} 不用多说

上一篇分析了go 原生的http服务。下面开始介绍go restful源码分析,有了上一篇的铺垫这篇文章讲解起来就很简单了。和go 的http一样启动监听:

wsContainer := restful.NewContainer() server := &http.Server{Addr: ":8080",Handler: wsContainer}

不用多说这个wsContainer肯定也是实现了Handler接口的。看代码:

func NewContainer() *Container {
    return &Container{
        webServices:            []*WebService{},ServeMux:               http.NewServeMux(),isRegisteredOnRoot:     false,containerFilters:       []FilterFunction{},doNotRecover:           true,recoverHandleFunc:      logStackOnRecover,serviceErrorHandleFunc: writeServiceError,router:                 CurlyRouter{},contentEncodingEnabled: false}
}

func (c *Container) ServeHTTP(httpwriter http.ResponseWriter,httpRequest *http.Request) {
    c.ServeMux.ServeHTTP(httpwriter,httpRequest)
}

上面有两个方式分别是定义container和实现Handler接口的的ServeHTTP()方法。那么当服务请求过来后就可以调用这个方法了。这个方法又调用c.ServeMux.ServeHTTP,是不是回到上一篇了,对,就练到一起了。

func (mux *ServeMux) ServeHTTP(w ResponseWriter,r *Request) {
    if r.RequestURI == "*" {
        if r.ProtoAtLeast(1,1) {
            w.Header().Set("Connection","close")
        }
        w.WriteHeader(StatusBadRequest)
        return
    }
    h,_ := mux.Handler(r)
    h.ServeHTTP(w,r)
}

那么先看他是怎么注册和查找方法的呢?

ws := new(restful.WebService)
    ws.
    Path("/users").
        Consumes(restful.MIME_XML,restful.MIME_JSON).
        Produces(restful.MIME_JSON,restful.MIME_XML) // you can specify this per route as well

    ws.Route(ws.GET("/{user-id}").To(u.findUser))
    ws.Route(ws.POST("").To(u.updateUser))
    ws.Route(ws.PUT("/{user-id}").To(u.createUser))
    ws.Route(ws.DELETE("/{user-id}").To(u.removeUser))

    container.Add(ws)

先看看Route方法,这里面记录所以路径和路径对应的方法,全部放到[]Route这个切片当中。

func (w *WebService) Route(builder *RouteBuilder) *WebService {
    w.routesLock.Lock()
    defer w.routesLock.Unlock()
    builder.copyDefaults(w.produces,w.consumes)
    w.routes = append(w.routes,builder.Build())
    return w
}

一个container里面多个webservice。可以通过Container的Add注册加入到webServices这个切片中。

func (c *Container) Add(service *WebService) *Container {
    c.webServicesLock.Lock()
    defer c.webServicesLock.Unlock()

    // if rootPath was not set then lazy initialize it
    if len(service.rootPath) == 0 {
        service.Path("/")
    }

    // cannot have duplicate root paths
    for _,each := range c.webServices {
        if each.RootPath() == service.RootPath() {
            log.Printf("[restful] WebService with duplicate root path detected:['%v']",each)
            os.Exit(1)
        }
    }

    // If not registered on root then add specific mapping
    if !c.isRegisteredOnRoot {
        c.isRegisteredOnRoot = c.addHandler(service,c.ServeMux)
    }
    c.webServices = append(c.webServices,service)
    return c
}

注册webservice的本质是注册各种handler看addHandler这个方法。

func (c *Container) addHandler(service *WebService,serveMux *http.ServeMux) bool {
    pattern := fixedPrefixPath(service.RootPath())
    // check if root path registration is needed
    if "/" == pattern || "" == pattern {
        serveMux.HandleFunc("/",c.dispatch)
        return true
    }
    // detect if registration already exists
    alreadyMapped := false
    for _,each := range c.webServices {
        if each.RootPath() == service.RootPath() {
            alreadyMapped = true
            break
        }
    }
    if !alreadyMapped {
        serveMux.HandleFunc(pattern,c.dispatch)
        if !strings.HasSuffix(pattern,"/") {
            serveMux.HandleFunc(pattern+"/",c.dispatch)
        }
    }
    return false
}

通过serveMux.HandleFunc注册,这也和上一篇的文章对应。不过这次注册的方法c.dispatch有点特别,具体看下面的方法,

func (c *Container) dispatch(httpWriter http.ResponseWriter,httpRequest *http.Request) {
    writer := httpWriter

    // CompressingResponseWriter should be closed after all operations are done
    defer func() {
        if compressWriter,ok := writer.(*CompressingResponseWriter); ok {
            compressWriter.Close()
        }
    }()

    // Instal panic recovery unless told otherwise
    if !c.doNotRecover { // catch all for 500 response
        defer func() {
            if r := recover(); r != nil {
                c.recoverHandleFunc(r,writer)
                return
            }
        }()
    }
    // Install closing the request body (if any)
    defer func() {
        if nil != httpRequest.Body {
            httpRequest.Body.Close()
        }
    }()

    // Detect if compression is needed
    // assume without compression,test for override
    if c.contentEncodingEnabled {
        doCompress,encoding := wantsCompressedResponse(httpRequest)
        if doCompress {
            var err error
            writer,err = NewCompressingResponseWriter(httpWriter,encoding)
            if err != nil {
                log.Print("[restful] unable to install compressor: ",err)
                httpWriter.WriteHeader(http.StatusInternalServerError)
                return
            }
        }
    }
    // Find best match Route ; err is non nil if no match was found
    var webService *WebService
    var route *Route
    var err error
    func() {
        c.webServicesLock.RLock()
        defer c.webServicesLock.RUnlock()
        webService,route,err = c.router.SelectRoute(
            c.webServices,httpRequest)
    }()
    if err != nil {
        // a non-200 response has already been written
        // run container filters anyway ; they should not touch the response...
        chain := FilterChain{Filters: c.containerFilters,Target: func(req *Request,resp *Response) {
            switch err.(type) {
            case ServiceError:
                ser := err.(ServiceError)
                c.serviceErrorHandleFunc(ser,req,resp)
            }
            // TODO
        }}
        chain.ProcessFilter(NewRequest(httpRequest),NewResponse(writer))
        return
    }
    wrappedRequest,wrappedResponse := route.wrapRequestResponse(writer,httpRequest)
    // pass through filters (if any)
    if len(c.containerFilters)+len(webService.filters)+len(route.Filters) > 0 {
        // compose filter chain
        allFilters := []FilterFunction{}
        allFilters = append(allFilters,c.containerFilters...)
        allFilters = append(allFilters,webService.filters...)
        allFilters = append(allFilters,route.Filters...)
        chain := FilterChain{Filters: allFilters,resp *Response) {
            // handle request by route after passing all filters
            route.Function(wrappedRequest,wrappedResponse)
        }}
        chain.ProcessFilter(wrappedRequest,wrappedResponse)
    } else {
        // no filters,handle request by route
        route.Function(wrappedRequest,wrappedResponse)
    }
}

这个里面方法里面通过SelectRoute选取对应的webservice和route,具体实现在:

func (c CurlyRouter) SelectRoute(
    webServices []*WebService,httpRequest *http.Request) (selectedService *WebService,selected *Route,err error) {

    requestTokens := tokenizePath(httpRequest.URL.Path)

    detectedService := c.detectWebService(requestTokens,webServices)
    if detectedService == nil {
        if trace {
            traceLogger.Printf("no WebService was found to match URL path:%sn",httpRequest.URL.Path)
        }
        return nil,nil,NewError(http.StatusNotFound,"404: Page Not Found")
    }
    candidateRoutes := c.selectRoutes(detectedService,requestTokens)
    if len(candidateRoutes) == 0 {
        if trace {
            traceLogger.Printf("no Route in WebService with path %s was found to match URL path:%sn",detectedService.rootPath,httpRequest.URL.Path)
        }
        return detectedService,"404: Page Not Found")
    }
    selectedRoute,err := c.detectRoute(candidateRoutes,httpRequest)
    if selectedRoute == nil {
        return detectedService,err
    }
    return detectedService,selectedRoute,nil
}

然后通过route.Function(wrappedRequest,wrappedResponse)调用具体的方法。

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读