This is a creation in Article, where the information may have evolved or changed.
First, the implementation process
To build a simple HTTP server:
package mainimport ( "log" "net/http")func main() { http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { w.Write([]byte("hello world")) }) log.Fatal(http.ListenAndServe(":8080", nil))}
Use http://127.0.0.1:8080/ to see the output
By tracing the Http.go package code, you can see that the execution process is basically as follows:
1. Create a Listener listening 8080 port
2. Enter the for loop and accept the request, no request is in the blocking state
3. Receive the request and create a Conn object to put into the goroutine processing (for high concurrency critical)
4. Resolve the request source information to obtain the request path and other important information
5. Request the Serverhttp method, the Responsewriter and request objects have been obtained through the previous step
func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) { //此handler即为http.ListenAndServe 中的第二个参数 handler := sh.srv.Handler if handler == nil { //如果handler为空则使用内部的DefaultServeMux 进行处理 handler = DefaultServeMux } if req.RequestURI == "*" && req.Method == "OPTIONS" { handler = globalOptionsHandler{} } //这里就开始处理http请求 //如果需要使用自定义的mux,就需要实现ServeHTTP方法,即实现Handler接口。 handler.ServeHTTP(rw, req)}
6. The logic to enter the Defaultservemux is to match the lookup handler in the map with the request path and leave it to handler
HTTP request processing process for more information, refer to the [Go WEB programming
"3.3 Go how to make Web Work" (HTTPS://GITHUB.COM/ASTAXIE/BUILD-WEB-APPLICATION-WITH-GOLANG/BLOB/MASTER/ZH/03.3.MD)
Second, Defaultservemux routing matching rules
Let's look at a few routing rules:
package mainimport ( "log" "net/http")func main() { //规则1 http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { w.Write([]byte("hello world")) }) //规则2 http.HandleFunc("/path/", func(w http.ResponseWriter, r *http.Request) { w.Write([]byte("pattern path: /path/ ")) }) //规则3 http.HandleFunc("/path/subpath", func(w http.ResponseWriter, r *http.Request) { w.Write([]byte("pattern path: /path/subpath")) }) log.Fatal(http.ListenAndServe(":8080", nil))}
Scenario One:
Visit: http://127.0.0.1:8080/
Return:hello world
Scenario Two:
Visit: http://127.0.0.1:8080/path
Return:pattern path: /path/
Scenario Three:
Visit: http://127.0.0.1:8080/path/subpath/
Return:pattern path: /path/
Scenario Four:
Visit: http://127.0.0.1:8080/hahaha/
Return:hello world
Let's start by explaining some of the rules, and then see how the code is implemented:
1. If there is a match in the path / , it will automatically add a matching rule without / suffix, and jump to path/ , explain the scenario two scenarios, why match to the/path/
2. I set so many rules why is the rule one generic to match the routing information that is not set, and does not affect the existing route, how the internal implementation?
2.1 Adding a routing rule
Let's look at two structs, which are the default routing rules:
type ServeMux struct { mu sync.RWMutex //处理并发,增加读写锁 m map[string]muxEntry //存放规则map,key即为设置的path hosts bool // whether any patterns contain hostnames(是否包含host)}type muxEntry struct { explicit bool //是否完全匹配 h Handler//相应匹配规则的handler pattern string//匹配路径}
By tracking http.HandleFunc to the following code, it is to struct add the rule to the above two:
Func (Mux *servemux) Handle (pattern string, handler handler) {Mux.mu.Lock () defer mux.mu.Unlock () if pattern = = "{Panic (" http:invalid pattern "+ pattern)} if handler = = nil {Panic (" Http:nil handler ")} Panic if mux.m[pattern].explicit {panic ("Http:multiple registrations for" + pattern) if it is already matched} Add a new matching rule mux.m[pattern] = Muxentry{explicit:true, H:handler, Pattern:pattern}//To determine if there is a host based on the first letter of Path If pattern[0]! = '/' {mux.hosts = true}//!! Here to see clearly is to achieve the situation of scenario two, see the conditions of the condition N: = Len (pattern) if n > 0 && pattern[n-1] = = '/' &&!mux.m[pattern[0:n- 1]].explicit{//If pattern contains a host name, strip it and use remaining//path for redirect. Path: = Pattern if pattern[0]! = '/' {//in pattern, at least the last character are a '/', so Strings. Index can ' t be-1. Path = pattern[strings. Index (Pattern, "/"):] } URL: = &url. Url{path:path} mux.m[pattern[0:n-1]] = Muxentry{h:redirecthandler (URL. String (), statusmovedpermanently), Pattern:pattern}}}
There is a Helpful behavior comment on the behavior, is to achieve the situation of two situations, he is to determine if the matching path is finally contained / , and there is no previous rule to add the removal of the backslash, it will automatically give him a 301 jump point/path/
2.2 Finding routing rules
The search for routing rules is to match the lookup from the ServeMux map in, to this handler and execute, but there are some processing mechanisms, such as how to ensure that access /path/subpath is the first match /path/subpath rather than match /path/ it?
When a request comes in, the method is tracked mux.match :
Process mux.ServerHTTP , mux.Handler mux.handlermux.match
func (mux *ServeMux) match(path string) (h Handler, pattern string) { var n = 0 for k, v := range mux.m { if !pathMatch(k, path) { continue } //如果匹配到了一个规则,并没有马上返回handler,而且继续匹配并且判断path的长度是否是最长的,这是关键!!! if h == nil || len(k) > n { n = len(k) h = v.h pattern = v.pattern } } return}
1. This explains why the exact path set is the optimal match, as it is judged by the length of the path.
Of course it explains why / you can match all (see pathMatch function to know, / match all, just this is the last to be matched successfully)
2. Get the handler of processing request, and then call to h.ServeHTTP(w, r) execute the corresponding handler method.
Wait a minute, where in handler has ServeHTTP this method??
Since the http.HandleFunc custom handler handler has been coerced to type at the time of invocation HandlerFunc , it 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)}
f(w,r)The implementation of the handler is realized.
Original address: silenceper.com