go-restful http server 框架 源码解析与说明

go-restful http server 框架 源码解析与说明

本文简单的介绍go-restrul 的使用,简单剖析go-restful 的代码。

目前开源界的go语言rest(http server)框架比较多,其中具有代表性的有beego, restful。 beego在国内非常著名, 功能完善且稳定。但是go-restful 则更轻量化, go-restful是按照java的JAX-RS使用golang语言的再实现。

go-restful 基本概念

go-restful 框架中最基础的几个概念包括: route, webservice, container。

route

route是http server的基本概念,是指一条http请求的URL, 根据此URL来确定那个函数为其服务。 go-restful中的route也是一样的,不过代码实现的时候跟准确的说法是注册路由。路由包含两种,RouterJSR311和CurlyRouter, CurlyRoute是base于routerJSR311的,但是支持正则表达式和动态参数,更加轻量化。
CurlyRouter在设定式包含:请求路径(URL Path),输入输出类型(JSON/YAML),请求对应的处理函数, 请求的参数,文档描述以及对应的回掉函数restful.RouteFunction,响应内容类型(Accept)等。

webservice

webservice实际上是一组route的集合。这组route拥有相同的rootpath或者base path,拥有相同的输入输出格式,基本一致的请求数据类型等一些通用的属性,举例说明:User为例,webservice是以/user/为基础路径的一个route集合,其下列的请求都是请求route相关操作的route。

换一种说法是将一组相关性非常强的request URL封装成为一个webserviice。举个例子来说明我们在公司开发过程中有项目信息和用户信息; 这样项目可以作为一个webservice, 用户可以作为另一个webservice。
webservice只是一组route的集合,其必须加入到container中才能够生效。

Container

Container 在http的角度就成为一个Server, 其包含一组webservice, 一个serveMUx,以及对应的routeselect负责请求派发。

三者之间的关系

一个container实际上是对外提供的一个(从http的基本角度看)http Server; 一个container里面可以有很多个webservice, 一个webservice可以看做是一组对象的服务,是一个种类的服务请求的一个合集或者看成是子服务; 一个 route则是单个请求的路由,一个webservice包括一组route,这一组路由有相同的base路径。其三者的相互关系如下图:


go-restful http server 框架 源码解析与说明_第1张图片

go-restrul 其他特点

go-restful的route 在注册的时候有很多其他描述,例如 Filter, Doc, Param, Metadata, Writes,Reads and Returns。
go-restful主要的Filter分为三类, 分别属于Container, webservice和route,其作用分别是其对应的Container, webservice和route.因此Filetr 分为Container.Filter, WebService.Filter跟Route.Filter。 Container.Filter作用在所有WebService之前, 而WebService.Filter作用与该WebService的所有Route之前。
其他的暂时先不做说明,(我也没弄明白)

从go-restrul example开始简单分析go-restful 源码

简单的介绍了go-restful框架之后这里使用简单的example来对go-restful的源码进行简单剖析,也做个简单的示例。

func main() {
wsContainer := restful.NewContainer()
wsContainer.Router(restful.CurlyRouter{})
u := UserResource{map[string]User{}}
u.Register(wsContainer)

log.Printf("start listening on localhost:8080")
server := &http.Server{Addr: ":8080", Handler: wsContainer}
log.Fatal(server.ListenAndServe())
}

以上是一个简单的基于go-restful的服务的main()函数,也就是主逻辑或者说使用总纲以及使用步骤。
一开始调用restful.NewContainer() 创建了一个webservice,然后调用container对象的Router(restful.CurlyRouter{})方法为container配置路由种类。该方法可以不用调用,因为restful.NewContainer()默认使用CurlyRouter, 但是你若想使用JSR311就必须调用Router(restful.RouterJSR311{})。
接下来创建了userResource用来存放User信息,然后u.Register是自己定义的一个函数,我们简单看一下,该函数:

func (u UserResource)Register(container *restful.Container) {
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)
}

以上函数主要是new了一个WebService,之后给Webservice配置了一组路由path, 然后将webservice添加到Container中。
上面的函数非常简单明了,再返回main()函数之后就定义了一个Server并把Container作为该Server的handler,之后就直接server.ListenAndServe()了。
再看WebService添加路由,函数调用非常简单:

ws.Route(ws.PUT("/{user-id}").To(u.createUser))

其中ws为WebService, Route是定义路由Path,ws.Put对应的是该Path的方法,之前的“golang http server 源码解析与说明”中也说过定义路由path的http方法, 然后在根据http header中的方法进行选择和过滤,这里的定义就非常简单,go-restful框架使用Route函数来帮你处理这个定义。 To() 定义了该路径下该方法的处理函数, 是一个http的 handler func(ResponseWriter, *Request)。
接下来我们来看一下该route函数:
route()函数的参数只有一个,是*RouteBuilder类型的。
以官方example为例来看:

ws.Route(ws.GET("/{user-id}").To(u.findUser).
    // docs
    Doc("get a users").
    Metadata(restfulspec.KeyOpenAPITags, tags).
    Writes([]User{}).
    Returns(200, "OK", []User{}))

其中:
“ws.GET(“/”).To(u.findAllUsers).
// docs
Doc(“get all users”).
Param(ws.PathParameter(“user-id”, “identifier of the user”).DataType(“integer”).DefaultValue(“1”)).
Metadata(restfulspec.KeyOpenAPITags, tags).
Writes(User{}). // on the response
Returns(200, “OK”, User{}).
Returns(404, “Not Found”, nil))”
是参数,让我们一层一层来看:

// GET is a shortcut for .Method("GET").Path(subPath
func (w *WebService) GET(subPath string) *RouteBuilder {
return new(RouteBuilder).typeNameHandler(w.typeNameHandleFunc).servicePath(w.rootPath).Method("GET").Path(subPath)
}  //定义了该routebuild的typeNameHandler 对应的的方法是GET, 路径是w.rootPath+subPath

// To bind the route to a function.
// If this route is matched with the incoming Http Request then call this function with the *Request,*Response pair. Required.
func (b *RouteBuilder) To(function RouteFunction) *RouteBuilder {
b.function = function
return b
} //定义了该routebuild对应的的function是 参数传递尽量的function

// Doc tells what this route is all about. Optional.
func (b *RouteBuilder) Doc(documentation string) *RouteBuilder {
b.doc = documentation
return b
}

// Param allows you to document the parameters of the Route. It adds a new Parameter (does not check for duplicates).
func (b *RouteBuilder) Param(parameter *Parameter) *RouteBuilder {
if b.parameters == nil {
    b.parameters = []*Parameter{}
}
b.parameters = append(b.parameters, parameter)
return b
}

// Writes tells what resource type will be written as the response payload. Optional.
func (b *RouteBuilder) Writes(sample interface{}) *RouteBuilder {
b.writeSample = sample
return b
}

func (b *RouteBuilder) Returns(code int, message string, model interface{}) *RouteBuilder {
err := ResponseError{
    Code:      code,
    Message:   message,
    Model:     model,
    IsDefault: false,
}
// lazy init because there is no NewRouteBuilder (yet)
if b.errorMap == nil {
    b.errorMap = map[int]ResponseError{}
}
b.errorMap[code] = err
return b
}

调用完以上所有函数后构成了一个RouteBuilder, 其实以上这些个函数时间就是给RouterBuilder的各个属性进行赋值, 之后再调用Route函数构成route,
// Route creates a new Route using the RouteBuilder and add to the ordered list of Routes.
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()) //append 添加webservice的路由,
return w
}
以上代码张还有一个builder.Build() 该函数是将创建来的RouterBuilder的信息转换成为route信息。,其函数内容如下:

// Build creates a new Route using the specification details collected by the RouteBuilder
func (b *RouteBuilder) Build() Route {
pathExpr, err := newPathExpression(b.currentPath)
if err != nil {
    log.Printf("[restful] Invalid path:%s because:%v", b.currentPath, err)
    os.Exit(1)
}
if b.function == nil {
    log.Printf("[restful] No function specified for route:" + b.currentPath)
    os.Exit(1)
}
operationName := b.operation
if len(operationName) == 0 && b.function != nil {
    // extract from definition
    operationName = nameOfFunction(b.function)
}
route := Route{
    Method:         b.httpMethod,
    Path:           concatPath(b.rootPath, b.currentPath),
    Produces:       b.produces,
    Consumes:       b.consumes,
    Function:       b.function,
    Filters:        b.filters,
    If:             b.conditions,
    relativePath:   b.currentPath,
    pathExpr:       pathExpr,
    Doc:            b.doc,
    Notes:          b.notes,
    Operation:      operationName,
    ParameterDocs:  b.parameters,
    ResponseErrors: b.errorMap,
    ReadSample:     b.readSample,
    WriteSample:    b.writeSample,
    Metadata:       b.metadata,
    Deprecated:     b.deprecated}
route.postBuild()
return route
}    

至此, 给webservice添加路由的工作完成。
接下来,我们看一下 container.Add(ws)的实际处理:

// Add a WebService to the Container. It will detect duplicate root paths and exit in that case.
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.dispatch
}
c.webServices = append(c.webServices, service)
return c
}

// addHandler may set a new HandleFunc for the serveMux
// this function must run inside the critical region protected by the webServicesLock.
// returns true if the function was registered on root ("/")
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
}

至此go-restrul 的路由注册于绑定函数结束。
在上面我们知道go-restful有三组Filters,分别是 containerFilters, WebServiceFilters,routeFliters, 他们怎样被执行,以及go-restful的路由如何解析下面函数中进行分析。

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
        }
    }()
}

// 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, Target: func(req *Request, 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)
}
        httpRequest)

}

其中上面webService, route,error=c.router.SelectRoute 是用来解析路由的,解析到那个webService以及对应的那个route, 那么该route对应的function即为该request请求的处理函数,既然如此为何还要找到webService呢?
因为WebService有Filter需要处理。 最后一部分(if–else–)添加所有的fliters(containerFilters, WebServcie.Filters, route.Filters)到allFilters,生产chain,然后调用chain.ProcessFilter函数进行处理。chain.ProcessFilter函数中调用完所有的Filter处理后,调用target函数,target函数为对应的route的function,其具体的代码如下:

func (f *FilterChain) ProcessFilter(request *Request, response *Response) {
if f.Index < len(f.Filters) {
    f.Index++
    f.Filters[f.Index-1](request, response, f)
} else {
    f.Target(request, response)
}
}

至此,go-restful的源码简单分析结束,纵观以上源码比较多,多级也比较清晰,使用非常简单, 我总结了一下其使用过程基本如下:


go-restful http server 框架 源码解析与说明_第2张图片

因此使用时只需要new一个Container, 给container添加几个WebService,给WebSerice添加几条路由即可使用。

go-restful 使用举

go-restful 最著名的例子便是docker领域的kubernetes,kubernetes 的api-server大量使用了go-restful框架。只是这个需要各位自己去看,这里不做分析。

你可能感兴趣的:(golang,http)