There are two core features in the Go language HTTP package: Conn,servemux.
Conn's Goroutine
Before we said that the user every request Conn is in a new goroutine to deal with, do not affect each other, will not be blocked, this is the high concurrency of Go. In func (SRV *server) Serve (l net. Listener) There is such a description in the error function
Serve accepts incoming connections on the Listener l, creating a new service goroutine for each.
The specific code is as follows:
c := srv.newConn(rw)c.setState(c.rwc, StateNew) // before Serve can returngo c.serve(ctx) //传递到对应的handle
Each request of the client creates a conn, which holds the information of the request, and then passes it to the corresponding handler, and the handler can read the corresponding header information, thus guaranteeing the independence of each request.
Customization of Servemux
In the Servehttp function, if we do not specify handler (that is, HTTP. Listenandserve The second parameter is nil), the system will specify the default Handler:defaultservemux, through the router to pass the information of this request to the back end of the processing function. So how is this router implemented? The following is when we do not specify the router, the system automatically sets the default router code.
func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) { handler := sh.srv.Handler if handler == nil { handler = DefaultServeMux //指定默认的 handler路由器 } if req.RequestURI == "*" && req.Method == "OPTIONS" { handler = globalOptionsHandler{} } handler.ServeHTTP(rw, req)}
The explanations and structure of SERVEMUX are as follows
ServeMux是一个HTTP请求多路复用器。它将每个传入请求的URL与已注册模式的列表进行匹配,并调用与URL最匹配的模式的处理程序。模式名称是固定的,有根的路径,如“/favicon.ico”,或带根的子树,如“/ images /”(请注意尾部斜杠)。较长的模式优先于较短的模式,因此如果有“/ images /”和“/ images / thumbnails /”注册的处理程序,则后面的处理程序将被调用以“/ images / thumbnails /”开头的路径和前者将收到“/ images /”子树中任何其他路径的请求。请注意,由于以斜杠结尾的模式命名为有根的子树,因此模式“/”匹配所有未与其他已注册模式匹配的路径,而不仅仅是具有Path ==“/”的URL。如果已注册子树并且收到命名子树根但没有其斜杠的请求,则ServeMux会将该请求重定向到子树根(添加尾部斜杠)。可以通过单独注册路径而不使用尾部斜杠来覆盖此行为。例如,注册“/ images /”会导致ServeMux将“/ images”请求重定向到“/ images /”,除非“/ images”已单独注册。模式可以选择以主机名开头,仅限制与该主机上的URL匹配。特定于主机的模式优先于一般模式,因此处理程序可以注册两个模式“/ codesearch”和“[codesearch.google.com/](http://codesearch.google.com/)”,而无需接管“[http://www.google.com/](http://www.google.com/)”的请求”。ServeMux还负责清理URL请求路径,重定向包含的任何请求。或..元素或重复斜杠到等效的,更清晰的URL。type ServeMux struct { mu sync.RWMutex // 锁,由于请求涉及到并发处理,因此这里需要一个锁机制 m map[string]muxEntry // 路由规则,一个 string 对应一个 mux 实体,这里的 string 就是注册的路由 hosts bool // 是否有任何模式包含主机名}
The muxentry structure is shown below
type muxEntry struct { h Handler // 这个路由表达式对应哪个 handler pattern string // 模式}
Next look at handler's explanation and definition
处理程序响应HTTP请求。ServeHTTP应该将回复标题和数据写入ResponseWriter,然后返回。返回请求完成的信号;在完成ServeHTTP调用之后或同时使用ResponseWriter或从Request.Body读取是无效的。根据HTTP客户端软件,HTTP协议版本以及客户端和Go服务器之间的任何中介,可能无法在写入ResponseWriter后从Request.Body读取。谨慎的处理程序应首先阅读Request.Body,然后回复。除了阅读正文外,处理程序不应修改提供的请求。如果ServeHTTP发生panic,服务器(ServeHTTP的调用者)假定panic的影响与活动请求隔离。它恢复了panic,将堆栈跟踪记录到服务器错误日志,并关闭网络连接或发送HTTP / 2 RST_STREAM,具体取决于HTTP协议。要中止处理程序以便客户端看到响应中断但服务器不记录错误,请使用值ErrAbortHandler进行panic。type Handler interface { ServeHTTP(ResponseWriter, *Request) //路由实现器}
In the Web program we wrote before, there is no Servehttp method to implement the Handler interface, so why can we use this method to access the route? Because we are using HTTP. The Handlefunc function.
Let's take a look at the concrete implementation of this function
//HandleFunc在DefaultServeMux中注册给定模式的处理函数。func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) { DefaultServeMux.HandleFunc(pattern, handler)}
Here Defaultservemux.handlefunc calls the following function
// HandleFunc为给定模式注册处理函数。func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) { mux.Handle(pattern, HandlerFunc(handler)) }
The above Handlerfunc (handler) calls the following code, we call Handlerfunc (f), forcing type conversion F to be the Handlerfunc type, so that F has the Servhttp method.
//HandlerFunc类型是一个允许将普通函数用作HTTP处理程序的适配器。 如果f是具有适当签名的函数,则HandlerFunc(f)是一个调用f的Handler。type HandlerFunc func(ResponseWriter, *Request)// ServeHTTP calls f(w, r).func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) { f(w, r) //调用 HandlerFunc}
In addition MUX. Handle calls the following function to register the corresponding handler and routing rules according to the given pattern, and if this rule exists, it will panic
func (mux *ServeMux) Handle(pattern string, handler Handler) { mux.mu.Lock() defer mux.mu.Unlock() if pattern == "" { panic("http: invalid pattern") } if handler == nil { panic("http: nil handler") } if _, exist := mux.m[pattern]; exist { panic("http: multiple registrations for " + pattern) } if mux.m == nil { mux.m = make(map[string]muxEntry) } mux.m[pattern] = muxEntry{h: handler, pattern: pattern} if pattern[0] != '/' { mux.hosts = true }}
Through the process above, we go through HTTP. The Handlefunc ("/", Mygowebservice) stores the corresponding routing rules in the set servehttp.
After the router has stored the corresponding routing rules, then the server receives the specific request and then how to choose the corresponding handler according to the URL?
The router calls Mux.handler (R) after it receives the request. Servehttp (W, R), the logic code distributed according to the rules is implemented in Mux.handler (R), and the relevant code is as follows:
//handler是Handler的主要实现。除了CONNECT方法之外,已知路径是规范形式。func (mux *ServeMux) handler(host, path string) (h Handler, pattern string) { mux.mu.RLock() defer mux.mu.RUnlock() // 特定于主机的模式优先于通用模式 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 the above code using the match function, according to the URL of the user request and the map stored in the router to match, when the match to return the stored handler, call the handler Servhttp interface can be executed to the corresponding function.
Custom Routing Features
So what do we do if we want to customize the routing function? We can define a handler ourselves and implement its Servehttp method.
import ( "net/http" "fmt")func main() { mux := &MyMux{} http.ListenAndServe(":9090",mux)}type MyMux struct {}func (p *MyMux)ServeHTTP(rw http.ResponseWriter,r *http.Request){ if r.URL.Path == "/" { myGoWebService(rw,r) return } http.NotFound(rw,r) return}func myGoWebService(rw http.ResponseWriter,request *http.Request) { fmt.Fprintf(rw,"Hello GoLang")}
If we enter http://localhost:9090/, then the browser will output the following results Hello Golang, if we enter the address is not "/", then the 404 page is not found
Summarize:
When we call HTTP. Handlefunc ("/", Mygowebservice), the bottom line actually does the following several things in order
① called Defaultservermux's handlefunc.
② called Defaultservermux's handle.
③ add handler and routing rules to the map[string]muxentry of Defaultservemux
When we call HTTP. Listenandserve (": 9090", nil), the bottom line actually does the following several things in order
① initialization of a server object
② Call Server Listenandserve ()
③ calls NET. Listen ("tcp", addr) Listening port
④ initiates a for{} loop in which the request is received by listener in the loop body
⑤ instantiates a conn for each request and opens a Goroutine to service the request Go C.serve ()
⑥ read the contents of each request W, err: = C.readrequest ()
⑦ determines if handler is empty, if no handler is set (this example does not set handler), handler is set to Defaultservemux
⑧ calls Handler's Servehttp:serverhandler{c.server}. Servehttp (W, W.req)
Finally, when the client sends the request, it selects the corresponding handler according to the URL and enters into the handler ServeHTTP:mux.handler (R). Servehttp (W, R)
Reference book: "Go Web Programming"