This is a creation in Article, where the information may have evolved or changed. Use it in go and its simple code to open a Web service. As follows:
test(){ http.HandleFunc("/", sayHello) err := http.ListenAndServe(":9090",nil) if err!=nil { log.Fatal("ListenAndServer:",err) }}func sayHello(w http.ResponseWriter, r *http.Request){ r.ParseForm() fmt.Println("path",r.URL.Path) fmt.Println("scheme",r.URL.Scheme) fmt.Fprintf"Hello Guest!")}
When using ListenAndServe
this method, the system assigns us a router, which is the default router used by the system DefaultServeMux
, and if the ListenAndServe
2nd parameter of this method is passed to nil, the system will be used by default DefaultServeMux
. Of course, you can also pass in a custom router here.
First look http.HandleFunc("/", sayHello)
, from the HandleFunc
method point, as follows:
func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) { DefaultServeMux.HandleFunc(pattern, handler)}
The method called here DefaultServeMux
HandleFunc
, which has two parameters, pattern
is a matching routing rule that handler
represents the corresponding processing method for the routing rule, and the processing method has two parameters.
In the code example we write, the corresponding, pattern
/
handler
corresponding sayHello
, when we enter in the browser http://localhost:9090
, the method is triggered sayHello
.
We DefaultServeMux
will continue to follow the HandleFunc
method, as follows:
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) { mux.Handle(pattern, HandlerFunc(handler))}
In this method, the router calls the Handle
method and takes note Handle
of the 2nd parameter of the method, forcing the previously passed response method to be cast to the handler
HandlerFunc
type.
HandlerFunc
What exactly is this type of thing? As follows:
type HandlerFunc func(ResponseWriter, *Request)
It seems that the types of methods we define SayHello
are similar. But!!!
This HandlerFunc
default implements the ServeHTTP
interface! So HandlerFunc
the object has a ServeHTTP
way! As follows:
// ServeHTTP calls f(w, r).func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) { f(w, r)}
This detail is important because this step concerns the question of whether the corresponding response method will be called when the routing rule matches! The invocation of this method is referred to in the next section.
Next, we'll go back to the way we're going to see mux
Handle
it, that's the code mux.Handle(pattern, HandlerFunc(handler))
. What does this piece of code do? The source code is as follows:
Func (Mux *servemux) Handle (pattern string, handler handler) {Mux.mu.Lock () defer mux.mu.Unlock ()ifPattern = =""{Panic ("Http:invalid pattern"+ pattern)}ifHandler = = Nil {Panic ("Http:nil handler") }ifmux.m[pattern].explicit {Panic ("Http:multiple registrations for"+ pattern)}ifMUX.M = = Nil {mux.m = make (Map[string]muxentry)} Mux.m[pattern] = muxentry{explicit:true, H:handler, Pattern:pattern}ifPATTERN[0]! ='/'{mux.hosts =true}//Helpful behavior://If pattern is/tree/, insert an implicit permanent redirect for/tree. It can be overridden by an explicit registration. N: = Len (pattern)ifn > 0 && pattern[n-1] = ='/'&&!mux.m[pattern[0:n-1]].explicit {//If pattern contains a host name, strip it and use remaining Path forredirect. Path: = PatternifPATTERN[0]! ='/'{//in pattern, at least the last character is 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} }}
Code is very much, in fact, the main thing to do is to DefaultServeMux
map[string]muxEntry
increase the corresponding routing rules and handler
.
map[string]muxEntry
What kind of a ghost?
map
is a Dictionary object that holds the key-value
.
[string]
Indicates that the dictionary key
is of string
type, and this key
value will save our routing rules.
muxEntry
is an instance object that holds the corresponding processing method for the routing rule.
Find the appropriate code, as follows:
//路由器type ServeMux struct { mu sync.RWMutex m map[string]muxEntry //路由规则,一个string对应一个mux实例对象,map的key就是注册的路由表达式(string类型的) hosts bool // whether any patterns contain hostnames}//muxEntrytype muxEntry struct { explicit bool h Handler //这个路由表达式对应哪个handler pattern string}//路由响应方法type Handler interface { ServeHTTP(ResponseWriter, *Request) //handler的路由实现器}
ServeMux
is the default router for this system.
Finally, summarize this section:
1. Callhttp.HandleFunc("/", sayHello)
2. Call DefaultServeMux
HandleFunc()
, put our definition of sayHello()
packaging into a HandlerFunc
type
3. Continue the DefaultServeMux
call Handle()
, DefaultServeMux
add the map[string]muxEntry
routing rules to the, and the correspondinghandler
OK, this part of the code is doing so much, the first part ends.
The second part is mainly to study this code, that is err := http.ListenAndServe(":9090",nil)
, ListenAndServe
this method. From this method point in, as follows:
func ListenAndServe(addr string, handler Handler) error { server := &Server{Addr: addr, Handler: handler} return server.ListenAndServe()}
In this method, an object is initialized, server
and then the method of invoking the server
object, ListenAndServe
in this method, is as follows:
func (srv *Server) ListenAndServe() error { addr := srv.Addr if"" { ":http" } ln, err := net.Listen("tcp", addr) if err != nil { return err } return srv.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)})}
In this method, a call is made, that is, the net.Listen("tcp", addr)
underlying TCP protocol is used to build a service and then monitor the ports we set up.
At the end of the code, the srv
method called is Serve
as follows:
Func (SRV *server) Serve (l net. Listener) Error {defer l.close ()iffn: =TestHookserverserve; fn! = Nil {fn (SRV, L)} var tempdelay time. Duration//How long-to-sleep on accept failureifERR: = Srv.setuphttp2_serve (); Err! = Nil {returnERR} srv.tracklistener (L,true) Defer Srv.tracklistener (L,false) Basectx: = Context. Background ()//base is always Background, per Issue 16220 CTX: = context. Withvalue (Basectx, Servercontextkey, srv) CTX = context. Withvalue (CTX, Localaddrcontextkey, L.addr ()) for{RW, E: = L.accept ()ifE! = Nil {select { Case<-srv.getdonechan ():returnerrserverclosed Default:}ifNE, OK: = E. (NET. ERROR); Ok && ne.Temporary() {ifTempdelay = = 0 {tempdelay = 5 * Time.millisecond}Else{Tempdelay *= 2}ifMax: = 1 * time. Second; Tempdelay > Max {tempdelay = max} srv.logf ("Http:accept error:%v; Retrying in%v ", E, Tempdelay) time. Sleep (Tempdelay)Continue}returne} tempdelay = 0 c: = Srv.newconn (rw) c.setstate (C.RWC, statenew)//before Serve canreturnGo C.serve (CTX)}}
The last 3 pieces of code are more important and are the embodiment of the high concurrency of the Go Language support, as follows:
returngo c.serve(ctx)
Above that big piece of code, the general meaning is to enter the method, the first open a for
loop, in the for
loop time accept request, after the request comes, will create one for each request, Conn
and then open a separate goroutine
, the request data as parameters thrown to this Conn
go to service: go c.serve()
. Each request of the user is in a new goroutine
go service, and each request does not affect each other.
In conn
the serve
method, there is a code that is important, as follows:
serverHandler{c.server}.ServeHTTP(w, w.req)
SaidserverHandler
Also
Implementation of the ServeHTTP
interface, the ServeHTTP
method is implemented as follows:
func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) { handler := sh.srv.Handler if handler == nil { handler = DefaultServeMux } if"*""OPTIONS" { handler = globalOptionsHandler{} } handler.ServeHTTP(rw, req)}
Here if it is handler
empty (this handler
can be understood as our custom router), we will use the system default DefaultServeMux
, the last Call of the code DefaultServeMux
ServeHTTP()
func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) { if"*" { if r.ProtoAtLeast(1, 1) { w.Header().Set("Connection""close") } w.WriteHeader(StatusBadRequest) return } h, _ := mux.Handler(r) //这里返回的h是Handler接口对象 h.ServeHTTP(w, r) //调用Handler接口对象的ServeHTTP方法实际上就调用了我们定义的sayHello方法}
After the router receives the request, if it is *
then close the link, if it is not *
called to mux.Handler(r)
return the route corresponding to the processing Handler
, and then execute the method, that is, handler
ServeHTTP
This code h.ServeHTTP(w, r)
, mux.Handler(r)
do what? As follows:
Func (Mux *servemux) Handler (R *request) (H Handler, pattern string) {ifR.method! ="CONNECT"{ifP: = CleanPath (R.url. Path); P! = R.url. Path {_, pattern = Mux.handler (r.host, p) URL: = *r.url URL. Path = PreturnRedirecthandler (URL. String (), statusmovedpermanently), pattern}}returnMux.handler (R.host, R.url. Path)}func (Mux *servemux) handler (host, path string) (H handler, pattern string) {Mux.mu.RLock () defer MUX.MU.RUNL Ock ()//Host-specific pattern takes precedence over generic onesifmux.hosts {h, pattern = mux.match (host + Path)}ifH = = Nil {h, pattern = Mux.match (path)}ifH = = Nil {h, pattern = Notfoundhandler (),""}return}func (Mux *servemux) match (Path string) (H Handler, pattern string) {var n = 0 forK, V: = Range mux.m {//MUX.M is the map of the system default routeif!pathmatch (k, path) {Continue}ifH = = Nil | | Len (k) > N {n = len (k) h = v.h pattern = V.pattern}}return}
It matches the map
stored in the router according to the user's requested URL
, and the matching succeeds returns the stored handler
, calling this handler The
's servehttp ()
can be executed to the appropriate processing method, which is actually the SayHello ()
that we just started to define, except this SayHello (
is handlerfunc
is wrapped again because Handlerfunc
implements the servehttp
interface, so the call Handlerfunc
object's servehttp ()
, our SayHello () is actually called inside of servehttp ()
.
Summarize:
1. Call http. Listenandserve (": 9090", nil)
2. Instantiate the server
3. Call server
, Listenandserve ()
4. Call the Serve
method of server
to turn on the for
Loop and accept the request in the Loop
5. Instantiate a single request for each Conn
, and open a goroutine
service for this request go C.serve ()
6. Read the contents of each request C.readrequest ()
7. Call servehttp ()
for Serverhandler
, and if handler
is empty, handler
is set to the system default router Defaultservemux
8. Call handler
servehttp ()
= = actually called defaultservemux
servehttp ()
9. In servehttp ()
The route handling handler
10. In route correspondence processing Handler
executes SayHello ()
There is one point to note:
DefaultServeMux
and the route corresponding to the processing methods handler
are implemented ServeHTTP
interface, they both have ServeHTTP
methods, but the method to achieve the purpose of different, in DefaultServeMux
the implementation of the ServeHttp()
route corresponding handler
processing ServeHttp()
.