Discussion on the use and implementation of go restful Framework

  • 2020-06-12 09:30:04
  • OfStack

REST (Representational State Transfer) is a widely used method to realize synchronous communication between distributed nodes in recent years. REST principle describes one interaction form of ES6en-ES7en in the network, that is, URL locates resources, and HTTP method describes the interaction form of operations. If the network interface for interactions between CS satisfies the REST style, it is called RESTful API. Here are the REST principles for understanding the RESTful architecture summary:

Resources on the network are identified by URI 1. Passing between client and server, some representation of this resource. The presentation layer can be json, text, base 2, or images. Through the four verbs of HTTP, the client operates on the server resources to achieve the state transformation of the presentation layer.

The reason why we want to design API of RESTful is that we use the data operation interface of HTTP's operating system 1 to limit URL as a resource, that is, each request is corresponding to a certain operation of a resource. This stateless design can realize the decoupling and separation of ES25en-ES26en and ensure the horizontal expansion capability of both ends of the system.

go-restful

go is package for building REST Web Services Google Go. go-restful defines Container WebService and Route3 important data structures.

Route represents one route and contains URL/HTTP/I/O type/callback handler RouteFunction WebService represents a service consisting of multiple Route that share the same Root as one Path Container represents a server, consisting of multiple WebService and one ES63en.ServerMux, distributed using RouteSelector

In the simplest use case, register the route to WebService, add WebService to Container, and distribute it by Container.


func main() {
  ws := new(restful.WebService)
  ws.Path("/users")
  ws.Route(ws.GET("/").To(u.findAllUsers).
    Doc("get all users").
    Metadata(restfulspec.KeyOpenAPITags, tags).
    Writes([]User{}).
    Returns(200, "OK", []User{}))

 container := restful.NewContainer().Add(ws)
 http.ListenAndServe(":8080", container)
}

container

container is written according to the router ServeMux of the standard library http, and it implements the Handler interface through the routing table of ServeMux. Please refer to the previous implementation of THE HTTP protocol and Go.


type Container struct {
  webServicesLock    sync.RWMutex
  webServices      []*WebService
  ServeMux        *http.ServeMux
  isRegisteredOnRoot   bool
  containerFilters    []FilterFunction
  doNotRecover      bool // default is true
  recoverHandleFunc   RecoverHandleFunction
  serviceErrorHandleFunc ServiceErrorHandleFunction
  router         RouteSelector // default is a CurlyRouter
  contentEncodingEnabled bool     // default is false
}

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

Add WebService to Container, webServices for internal maintenance cannot have duplicate RootPath,


func (c *Container)Add(service *WebService)*Container {
  c.webServicesLock.Lock()
  defer c.webServicesLock.Unlock()
  if !c.isRegisteredOnRoot {
    c.isRegisteredOnRoot = c.addHandler(service, c.ServeMux)
  }
  c.webServices = append(c.webServices, service)
  return c
}

Added to container and registered to mux is the function dispatch, which is responsible for distribution according to the rootPath of the different WebService.


func (c *Container)addHandler(service *WebService, serveMux *http.ServeMux)bool {
  pattern := fixedPrefixPath(service.RootPath())
  serveMux.HandleFunc(pattern, c.dispatch)
}

webservice

Each group of webservice represents one service sharing rootPath, where rootPath is set through ES109en.Path ().


type WebService struct {
  rootPath    string
  pathExpr    *pathExpression 
  routes     []Route
  produces    []string
  consumes    []string
  pathParameters []*Parameter
  filters    []FilterFunction
  documentation string
  apiVersion   string

  typeNameHandleFunc TypeNameHandleFunction
  dynamicRoutes bool
  routesLock sync.RWMutex
}

The routes registered through Route ultimately constitute the Route structure, which is added to WebService's 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())
  return w
}

route

By constructing Route information from RouteBuilder, Path combines rootPath and subPath. Function is the route Handler, or processing function, which is added via ws.Get (subPath).To (function). Filters implements something like the gRPC interceptor, which is also similar to ES138en-ES139en's chain.


type Route struct {
  Method  string
  Produces []string
  Consumes []string
  Path   string // webservice root path + described path
  Function RouteFunction
  Filters []FilterFunction
  If    []RouteSelectionConditionFunction
  // cached values for dispatching
  relativePath string
  pathParts  []string
  pathExpr   *pathExpression
  // documentation
  Doc           string
  Notes          string
  Operation        string
  ParameterDocs      []*Parameter
  ResponseErrors     map[int]ResponseError
  ReadSample, WriteSample interface{} 
  Metadata map[string]interface{}
  Deprecated bool
}

dispatch

The main function of the server side is routing and distribution. http package implements one ServeMux, go-restful encapsulates multiple services on this basis, how to distribute routes to webservice from container, and then to specific processing functions by webservice. This is all done in dispatch.

SelectRoute select matching WebService and matching Route from the registered WebService based on Req. Where the routing selector defaults to CurlyRouter. Parse pathParams and process the request for wrap and the corresponding handler to the routing. If there is an filters definition, then chain processing.

func (c *Container)dispatch(httpWriter http.ResponseWriter, httpRequest *http.Request) {
  func() {
    c.webServicesLock.RLock()
    defer c.webServicesLock.RUnlock()
    webService, route, err = c.router.SelectRoute(
      c.webServices,
      httpRequest)
  }()

  pathProcessor, routerProcessesPath := c.router.(PathProcessor)
  pathParams := pathProcessor.ExtractParameters(route, webService, httpRequest.URL.Path)
  wrappedRequest, wrappedResponse := route.wrapRequestResponse(writer,
  httpRequest, pathParams)

  if len(c.containerFilters)+len(webService.filters)+len(route.Filters) > 0 {
    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 {
    route.Function(wrappedRequest, wrappedResponse)
  }
}

go-chassis

rest-server the ES173en-server implementation is a 1-layer encapsulation on ES175en-restful. At the time of Register, the registered schema should be resolved into routes and registered in webService. When Start starts server, container. Add(r. ws), and at the same time, container will be handed to ES189en. Server and finally start ListenAndServe.


type restfulServer struct {
  microServiceName string
  container    *restful.Container
  ws        *restful.WebService
  opts       server.Options
  mux       sync.RWMutex
  exit       chan chan error
  server      *http.Server
}

According to Method, handle of different methods is registered with WebService, and routes information read from schema includes Method, Func and PathPattern.


type Container struct {
  webServicesLock    sync.RWMutex
  webServices      []*WebService
  ServeMux        *http.ServeMux
  isRegisteredOnRoot   bool
  containerFilters    []FilterFunction
  doNotRecover      bool // default is true
  recoverHandleFunc   RecoverHandleFunction
  serviceErrorHandleFunc ServiceErrorHandleFunction
  router         RouteSelector // default is a CurlyRouter
  contentEncodingEnabled bool     // default is false
}
0

It's really quite simple, so I won't write it. I'm so sleepy today.

legacy

Use, Reflection and performance of reflect in routing registration route select how to ensure the processing speed when it comes to fuzzy matching pathParams analytical

Related articles: