Overview of rest frameworks

To generate an API service, go Zero uses the command-line tool goctl. Its main function is as follows:

func main(a) {
	flag.Parse()

	var c config.Config
	conf.MustLoad(*configFile, &c)

	ctx := svc.NewServiceContext(c)
	server := rest.MustNewServer(c.RestConf)
	defer server.Stop()

	handler.RegisterHandlers(server, ctx)

	fmt.Printf("Starting server at %s:%d... \n", c.Host, c.Port)
	server.Start()
}
Copy the code
  1. Parsing configuration files
  2. Pass in the configuration file and initialize itserviceContext
  3. Initialize therest server
  4. willcontextinjectionserverIn:
    1. Registered routing
    2. willcontextIn startendpointSimultaneously inject intorouteramong
  5. Start theserver

Let’s explain the design principle step by step! Let’s Go!

Web framework

From everyday development experience, a good Web framework generally meets the following characteristics:

  1. Route matching/multiple route support
  2. Support for custom middleware
  3. Framework and business development are completely decoupled, making it easy for developers to develop quickly
  4. Parameter verification/matching
  5. Monitor, log, and indicator self-check functions
  6. Service self-protection (fusing/current limiting)

Go – zero rest design

Github.com/zeromicro/g…

An overview of

  1. Initialize the resource with context (different from gin’s context) → save it inserviveCtx(For resource pooling, let the resource handle itself,serviveCtxJust entry and sharing points)
  2. Separate router declaration files and add router groups to make code structure easier for developers
  3. Several built-in middleware: monitoring/fusing/authentication, etc
  4. Using goCTL CodeGen + Option design mode, it is convenient for developers to control the access of some middleware by themselves

The figure above describes the rest pattern for processing requests and most of the processing paths.

  1. The middleware built into the framework already handles most of the service self-processing logic for developers
  2. At the same time go to zerobusiness logicAlso gives developers out-of-the-box components (DQ, FX, etc.)
  3. From the development mode to help developers only need to focus on their ownbusiness logicAnd the preparation of required resources

Let’s take a closer look at how rest gets started.

Start the process

The figure above describes the overall server startup module and general flow. The rest implementation is analyzed as follows:

  1. Http. server based encapsulation and transformation: Separate Engine (core web framework) from Option
  2. Multi – route matching adopts the radix-tree construction
  3. Middleware adopts onion model →[]Middleware
  4. HTTP parse and match verification →httpx.Parse()
  5. Metrics are collected during the request (createMetrics()) and monitoring buried sites (Prometheus)

Server engine assembly

Click on the larger image to view

Engine runs through the server lifecycle:

  1. The router carries a developer-defined path/handler, which is executed in the final router.handle()
  2. Registered custom middleware + framework middleware, executed in front of Router Handler Logic

Here we go: The granularity of go-Zero processing is on the route, and encapsulation and processing are performed layer by layer along the route

Routing matching

So when a request comes in, how does it get to the routing layer in the first place?

First of all, in the development of the most primitive HTTP server, there is such a code:

type helloHandler struct{}

func (h *helloHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    w.Write([]byte("Hello, world!"))}func main(a) {
    http.Handle("/", &helloHandler{})
    http.ListenAndServe(": 12345".nil)}Copy the code

HTTP.ListenAndServe() is executed internally: server.listenAndServe ()

Let’s see how this works in REST:

The incoming handler is the router generated by router.newrouter (). The Router hosts the entire set of server handlers.

The http.Server structure is initialized to inject a handler into it:

type Server struct{... Handler Handler }func start(... , handler http.Handler, runfunc(srv *http.Server) error) (err error) {
	server := &http.Server{
		Addr:    fmt.Sprintf("%s:%d", host, port),
		Handler: handler,
	}
	...
	return run(server)
}
Copy the code

After http.Server receives req, handler.ServeHTTP(rW, req)

So the built-in router also needs to be implementedServeHTTP. How is the Router itself implementedServeHTTP: Searches for the matched route and executes the Handle Logic corresponding to the route.

Analytical parameters

Parsing parameters is a basic capability that HTTP frameworks need to provide. In the code generated by Goctl Code Gen, the handler layer has integrated the req argument parse function:

// generate by goctl
func QueryAllTaskHandler(ctx *svc.ServiceContext) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		// custom request in .api file
		var req types.QueryAllTaskRequest
		// parse http request
		iferr := httpx.Parse(r, &req); err ! =nil {
			httpx.Error(w, err)
			return
		}

		l := logic.NewEventLogic(r.Context(), ctx)
		resp, err := l.QueryAllTask(req)
		baseresponse.FormatResponseWithRequest(resp, err, w, r)
	}
}
Copy the code

Enter httpx.parse () to Parse the following blocks:

Github.com/zeromicro/g…

  1. Parsing path
  2. Parsing form forms
  3. Parsing the HTTP header
  4. Parsing json

The parameter verification function in Parse() can be found in:

Go – zero. Dev/cn/API – “gramm… Tag modifier in

Tips

Learn source code recommendation fork out to see while writing comments and experience, you can deepen understanding, later use this feature can also go back to read.

The project address

Github.com/zeromicro/g…

Welcome to Go-Zero and star support us!

Wechat communication group

Pay attention to the public account of “micro-service Practice” and click on the exchange group to obtain the QR code of the community group.