Deep understanding of dispatcher in go language

Time:2020-4-3

introduce

Go uses goroutines to handle the read and write events of connection without blocking:


c, err := srv.newConn(rw)
  if err != nil {
    continue
  }
  go c.serve()

C is the created connection, which saves the information of the request, and then passes it to the corresponding handler. The handler can read the header information of the request, ensuring the independence between the requests.

Servemux in go

The above code mentions the C (which is the connection). Serve () method. In fact, the internal is to call the default router of HTTP packet, through which the information of this request is passed to the back-end processing function.

The default router, servermux, has the following structure:

type ServeMux struct {
 Mu sync.rwmutex // lock. Since the request involves concurrent processing, a lock mechanism is needed here
 M map [string] muxentry // routing rules. A string corresponds to a MUX entity. The string here is the registered routing expression
 Hosts bool // whether host information is included in any rule
}

Let’s look at muxentry:

type muxEntry struct {
 Explicit bool // exact match
 H handler // which handler does this routing expression correspond to
 Pattern string // matching string
}

Next, let’s look at the definition of handler:

type Handler interface {
 Servehttp (responsewriter, * request) // routing implementer
}

The handler is an interface, but the sayhelloname function in the previous section does not implement the serverhttp interface, which can still be added to the routing table. The reason is that there is a handlerfunc in the HTTP package, and the sayhelloname function we defined is the result of the handlerfunc call, and this type implements the serverhttp interface by default, that is, we call theHandlerFunc(f), force type conversion f to handlerfunc type, so f has the serverhttp method.


type HandlerFunc func(ResponseWriter, *Request)
// ServeHTTP calls f(w, r).
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
  f(w, r)
}

Let’s take a look at the official notes of handlerfunc:

The handlerfunc type is an adapter that allows ordinary functions to be used as HTTP handlers. If f is a function with the appropriate signature,HandlerFunc(f)Is the handler that calls F.

Proper signature, because the author’s level is not deep (after all, my native language is Java), guess that it should refer to the function’s parameters and return value, that is to say: if the function’s parameters are two, responsewriter and a pointer to request, and the return value is a void type function, it can be forced to handlerfunc, and the handler in F in the final call The method of the interface is servehttp.

After the corresponding routing rules are stored in the router, how are the specific requests distributed? See the following code. The default router implements serverhttp:


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

After the router receives the request as shown above, if it is * then close the link, otherwise callmux.Handler(r)Return the processing handler corresponding to the set route, and then executeh.ServeHTTP(w, r) 。 To glance atServeMUX.Handler(*request)Official documents of:

Handler returns the handler used for the given request, please consultr.Methodr.Hostandr.URL.Path。 It always returns a non nil handler. If the path is not in its canonical form, the handler is an internally generated handler that redirects to the canonical path.

The handler also returns the registration pattern that matches the request, or, in the case of internally generated redirects, the pattern that matches after following the redirects.

If there is no registration handler for the request, the handler returns the page not found handler and empty mode.

To put it bluntly, a handler is returned according to the method, host of the request and the path of the request URL. This handler is the handler we said. Then look at the method of the handler interface, we will know that it will eventually run into our sayhelloname ~. Let’s see.ServeMux.Handler(*request)Implementation:


func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) {
 if r.Method != "CONNECT" {
 if p := cleanPath(r.URL.Path); p != r.URL.Path {
  _, pattern = mux.handler(r.Host, p)
  return RedirectHandler(p, StatusMovedPermanently), pattern
 }
 }  
 return mux.handler(r.Host, r.URL.Path)
}
func (mux *ServeMux) handler(host, path string) (h Handler, pattern string) {
 mux.mu.RLock()
 defer mux.mu.RUnlock()
 // Host-specific pattern takes precedence over generic ones
 if mux.hosts {
 h, pattern = mux.match(host + path)
 }
 if h == nil {
 h, pattern = mux.match(path)
 }
 if h == nil {
 h, pattern = NotFoundHandler(), ""
 }
 return
}

In order to keep the reader from getting confused, let’s take a look at the match method, which is a private method, iterating the map in MUX:


func (mux *ServeMux) match(path string) (h Handler, pattern string) {
 var n = 0
 for k, v := range mux.m {
 if !pathMatch(k, path) {
  continue
 }
 if h == nil || len(k) > n {
  n = len(k)
  h = v.h
  pattern = v.pattern
 }
 }
 return
}

After matching, return the stored handler, and call the Server HTTP interface of the handler to execute the corresponding function.

In fact, the second parameter of the router listenandserve, which supports external implementation, is used to configure the external router. It is a handler interface, that is, as long as the external router implements the handler interface, we can implement the custom routing function in the servehttp of our own router.

We implement a simple Router:


package main
import (
 "fmt"
 "net/http"
)
type MyMux struct {}
func (p *MyMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 if r.URL.Path == "/" {
 sayhelloName(w, r)
 return
 }
 http.NotFound(w, r)
 return
}
func sayhelloName(w http.ResponseWriter, r *http.Request) {
 fmt.Fprintf(w, "Hello myroute!")
}
func main() {
 mux := &MyMux{}
 http.ListenAndServe(":9090", mux)
}

After analyzing the HTTP package, let’s review the whole code execution process:

1. Call firstHttp.HandleFunc, did several things in order:

  • Handlefunc with defaultservemux called
  • Handle with defaultservemux called
  • To defaultservemux’smap[string]muxEntryAdd corresponding handler and routing rule in

2. Next callhttp.ListenAndServe(“:9090”, nil), did several things in order:

  • Instantiate server
  • Call listenandserve() of server
  • Call net.listen (“TCP”, addr) to listen to the port
  • Start a for loop and accept the request in the loop body
  • Instantiate a conn for each request and open a goroutine to serve the requestgo c.serve()
  • Read the content of each requestw, err := c.readRequest()
  • Judge whether the handler is empty. If the handler is not set (in this case, the handler is not set), the handler is set to defaultservemux
  • Call serverhttp of handler
  • In this case, go to defaultservemux.servehttp
  • Select the handler according to the request, and enter the Server HTTP of the handler,mux.handler(r).ServeHTTP(w, r)
  • Select handler:
  • Determine whether there is a route to meet the request (loop through the muxentry of servermux)
  • If any route is satisfied, call servehttp of the route handler
  • If no route is satisfied, call servehttp of notfoundhandler

summary

The above is the whole content of this article. I hope the content of this article can help you in your study or work. If you have any questions, you can leave a message and exchange. Thank you for your support for developpaer.

Recommended Today

Python basics Chinese series tutorial · translation completed

Original: Python basics Python tutorial Protocol: CC by-nc-sa 4.0 Welcome anyone to participate and improve: a person can go very fast, but a group of people can go further. Online reading Apache CN learning resources catalog introduce Seven reasons to learn Python Why Python is great Learn Python introduction Executing Python scripts variable character string […]