An in depth understanding of Dispatcher in the Go language
- 2020-06-07 04:37:01
- OfStack
introduce
Go USES goroutines to handle connection read and write events without blocking:
c, err := srv.newConn(rw)
if err != nil {
continue
}
go c.serve()
c is the connection created. It saves the information of the request and then passes it to the corresponding handler. handler can read the header information of the request, ensuring the independence between the requests.
The ServeMux Go
The c (this c is connection).serve () method is mentioned in the code above. Internally, the default router of the http package is called, and the router passes the information of the request to the backend handler.
The default router, ServeMux, has the following structure:
type ServeMux struct {
mu sync.RWMutex // Locks, because the request involves concurrent processing, are required here 1 A locking mechanism
m map[string]muxEntry // Routing rules, 1 a string The corresponding 1 a mux Entities, here string Is the registered routing expression
hosts bool // Whether in an arbitrary rule with host information
}
Let's look at 1 muxEntry:
type muxEntry struct {
explicit bool // Whether it matches exactly
h Handler // Which routing expression corresponds to handler
pattern string // Matching string
}
Then take a look at the definition of Handler:
type Handler interface {
ServeHTTP(ResponseWriter, *Request) // Routing implementor
}
Handler is an interface, but the sayhelloName function in the previous section does not implement the INTERFACE ServeHTTP and can still be added to the routing table. The reason is that there is another HandlerFunc in the http package. The function sayhelloName we define is the result of the call to HandlerFunc
HandlerFunc(f)
, casts f to the HandlerFunc type, so f has the ServeHTTP 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 an appropriate signature,
HandlerFunc(f)
It's Handler calling f.
Appropriate signature, one is because the author deep (after all, is I this life language java), guess is under 1 refers to the function parameters and return values, that is to say: if a function parameter is two, respectively is a pointer to the Request ResponseWriter and, and the return value is the function of void type, can be strong to HandlerFunc, and in the final call f method namely ServeHttp Handler interface.
After the routing rules are stored in the router, how are the specific requests distributed? See the following code. The default router implements ServeHTTP:
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, close the link if it is *, or call
mux.Handler(r)
Returns the processing Handler corresponding to the set route, and executes
h.ServeHTTP(w, r)
. See one
ServeMUX.Handler(*request)
Official documents:
Handler returns the handler for the given request, consult
r.Method
.
r.Host
and
r.URL.Path
. It always returns a non-ES90en handler. If the path is not in its canonical form, the handler is a generated handler that is redirected to the canonical path.
Handler also returns the registration pattern that matches the request, or, in the case of an internally generated redirect, the pattern that matches after the redirect.
If there is no registration handler for the request, Handler returns the "Not found Page" handler and empty mode.
To be clear, request returns a handler based on the path of method, host, and the requested URL. This handler is what we said Handler. If we look at the method of the Handler interface, we know that it will end up in sayhelloName. Let's take a look at
ServeMux.Handler(*request)
The implementation of the:
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 not to confuse the reader, let's look at the first match method, which is a private method that iterates over 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
}
Return the stored handler and call the ServeHTTP interface of handler to execute the function.
Go actually supports the external implementation of the router ListenAndServe the second parameter is used to configure the external router, it is an Handler interface, that is, the external router as long as the implementation of Handler interface can be, we can implement custom routing function in the implementation of the router ServeHTTP.
We realize 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)
}
With an analysis of the http package, let's now tease out the entire code execution process:
1. Call first
Http.HandleFunc
, in order to do a few things:
HandlerFunc(f)
0
To add the corresponding handler and routing rules
2. Call next
http.ListenAndServe(“:9090”, nil)
, did a few things in order:
go c.serve()
Read the contents of each request
w, err := c.readRequest()
Determine if handler is empty. If handler is not set (handler is not set in this example), handler is set to DefaultServeMux
ServeHttp calling handler
In this case, let's go to ES168en.ServeHttp
Select handler according to request, and enter the ServeHTTP of this handler,
mux.handler(r).ServeHTTP(w, r)
Choose handler:
Determine if any routes satisfy this request (muxEntry for looping through ServerMux)
If any route satisfies, call ServeHttp of this route handler
If no route is satisfied, call ServeHttp of NotFoundHandler
conclusion