This is a creation in Article, where the information may have evolved or changed.
1. Start by looking at a simple Web service
package Main import ( "io" "net/http" "Lo G ") //Hello World, the Web server func HelloServer (w http. Responsewriter, req *http. Request) {io. WriteString (W, "Hello, world!\n" )}func main () { http. Handlefunc ( "/hello" , HelloServer) Err: = http. Listenandserve ( ": 12345" , nil ) if err! = nil {log. Fatal ( "Listenandserve:" , Err)}}
The above code only relies on Go's standard package and does not rely on any third-party packages.
Enter \ "Http://localhost:12345/hello\" in the browser to see "Hello world!".
We see the code above, to write a Web server is very simple, as long as the call HTTP packet two functions can be
.
2. Analyze how to start this service there are two aspects, one is the way out of the language of the web work, and the other is the bottom of the Go language implementation The way the Web works is not discussed here, and the implementation of GO is discussed here.
Go HTTP Package execution Flow:
(1) Create a listen Socket, listen to the specified port, wait for the client request to arrive. The
(2) Listen socket accepts the client's request, obtains the customer socket, and then communicates with the client through the customer socket.
(3) processing the client's request, first read the HTTP request from the client socket protocol header, if it is the post method, you may also want to read the data submitted by the client, and then to the corresponding handler processing request, handler processing finished ready for the client to prepare the data , which is addressed to clients via the client socket.
In this whole process we just need to know the following three questions, and we know how go makes the Web run
(1) How to listen to the port?
(2) How do I receive client requests?
(3) How to assign handler?
actually these three features are in HTTP. Listenandserve (": 12345", nil) This function is implemented, by looking at the source of go, this function is implemented as follows:
handler to handle requests// on incoming connections. Handler is typically nil,// in which case the DefaultServeMux is used.func ListenAndServe(addr string, handler Handler) error { server := &Server{Addr: addr, Handler: handler} return server.ListenAndServe()}
Generates a server object and then calls the Listenandserve () method of the server object
//Listenandserve listens onThe TCP network address srv. Addr and Then//Calls Serve to handle requests onIncoming connections. If//Srv. Addr isBlank": http" isUsed.func (SRV *server) listenandserve () error {addr: = srv. AddrifAddr = =""{addr =": http"} ln, err: = Net. Listen ("TCP", addr)ifErr! = Nil {returnERR}returnSrv. Serve (Tcpkeepalivelistener{ln. ( *net. TcpListener)})}
NET was called. Listen ("tcp", addr), which is the bottom layer, builds a service with the TCP protocol and then monitors the ports we set up. (TCP protocol I basically do not know, and do not seem to know that the problem does not have any impact)
Look again at the Serve method:
Serve accepts incoming connections on the Listener L, creating a//new service Goroutine for each. The service GoroutinesReadRequests andThen call SRV. Handler to reply to Them.func (SRV*server) Serve (l net. Listener) Error {defer l.close () var tempdelay Time. Duration//How long toSleepOnAcceptFailure for{RW, E: = L.accept ()ifE! = Nil {if NE, 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 =0C, Err: = Srv.newconn (rw)ifErr! = Nil {Continue} c.setstate (C.RWC, statenew)//before Serve canreturnGo C.serve ()}}
The code starts a For loop, first receives the request through listener, Second creates a conn, and finally opens a goroutine, throwing the requested data as a parameter to the Conn to service: Go C.serve (). This is the high concurrency of the embodiment, the user every request is in a new goroutine to service, not affect each other. The rest of the code in the For loop is implemented in the event of an error in receiving a request, continuing into the loop after a certain period of time, without causing the program to exit because of an error.
So how do you assign the appropriate function to handle the request? Conn first parses Request:c.readrequest () and then gets the corresponding Handler:handler: = C.server.handler, that is, we just called the function listenandserve when the second argument, we passed the previous example is nil, which is empty, then the default Handler=defaultservemux, So what is this variable used for? Yes, this variable is a router that matches the URL to jump to its corresponding handle function. So, did we set this up? There, the first sentence in the code we called called HTTP. Handlefunc ("/", HelloServer), this function is to register the request/routing rule, when the request URL is "/", the route will go to the function Helloserver,defaultservemux call servehttp method, The inside of this method is actually called HelloServer itself, finally by writing response information feedback to the client. Note that this is just a router for assigning handler, and let's look at how this router is implemented.
3. Implementation of the Router
In this example, HTTP. Handlefunc ("/hello", HelloServer) This line of code registers the handler function under the specified path.
typeServemuxstruct{mu sync. Rwmutex//Lock, involving multithreadingMMap[string]muxentry hostsBOOL //Whether any patterns contain hostnames}typeMuxentrystruct{ExplicitBOOLH Handler Patternstring}//Objects Implementing the Handler interface can be//registered to serve a particular path or subtree//In the HTTP server.////Servehttp should write reply headers and data to the Responsewriter//And then return. Returning signals that the request is finished//And that the HTTP server can move on to the next request on//the connection.////If servehttp panics, the server (the caller of Servehttp) assumes//That the effect of the panic is isolated to the active request.//It recovers the panic, logs a stack trace to the server error log,//And hangs up the connection.//typeHandlerInterface{servehttp (Responsewriter, *request)}func(Mux *servemux) Servehttp (w responsewriter, R *request) {ifR.requesturi = ="*"{ifR.protoatleast(1,1) {W.header (). Set ("Connection","Close")} w.writeheader (Statusbadrequest)return} h, _: = Mux. Handler (R) H.servehttp (W, R)}
Map[string]muxentry, save the path name corresponding method, a path corresponding to a method, method entity Muxentry The most critical method property handler,
Here is the definition of handler, which has one method: Servehttp (Responsewriter,*request). This method is our incoming SayHello method, which is the business logic method that we really want to do, he has only two parameters, one is the user's request information, and one is used to write the result after we have finished processing the business logic. With func (Mux *servemux) servehttp (w responsewriter, R *request), we can see that Servemux also implements the handler interface, but Servemux is a special handler interface, Instead of dealing with the actual business logic, he finds the appropriate handler for routing information in the handler dictionary he stores internally. Here we solve the problem we mentioned earlier: C.serve () is called the Servemux Servehttp method to find our HelloServer method and execute.
It is necessary to note that the default route for the go language is Defaultservemux, and when we pass nil, we use this routing rule. Someone here would be curious that the second parameter of this function should be a handler, but what we are passing is just a normal function, and how can we recognize it? Take a look at the following code:
Handlefunc registers theHandlerfunction forThe given pattern//inchThe defaultservemux.//the documentation forServemux explains how Patterns is matched. Func Handlefunc (Pattern string,HandlerFunc (Responsewriter, *request)) {Defaultservemux.handlefunc (pattern,Handler)}//the Handlerfunc type isAn adapter toAllow the use ofOrdinary functions asHTTP handlers.IfF isA function// withThe appropriate signature, Handlerfunc (f) isa//HandlerObject that calls F.type Handlerfunc func (responsewriter, *request)//Servehttp calls F (W, R). Func (f Handlerfunc) serveht TP (w responsewriter, R *request) {f (W, R)}//Handlefunc registers theHandlerfunction forThe given Pattern.func (Mux *servemux) Handlefunc (pattern string,HandlerFunc (Responsewriter, *request)) {Mux. Handle (Pattern, Handlerfunc (Handler))}
This method invokes the Handlefunc method of Servemux (Defaultservemux is an instance of Servemux), which does a forced type conversion: Handlerfunc (handler), Convert our incoming HelloServer method to Handlerfunc (note that handlefunc differs from Handlerfunc, which has an R-letter more than the former). Handlerfunc implements the handler interface, it can be servemux to find and invoke its Servehttp method, which is actually called the HelloServer method.
From the result, this design allows us to implement a Web server only one line of code, very convenient. But the way of implementation is a bit of a detour.
4. Custom Route
Go back to the beginning of the Listenandserve () function, where the second parameter can be used to configure the external route, as long as the external route implements the handler interface.
Below is an example of Go_web programming:
package mainimport ( "FMT") /span> "net/http" ) type Mymux struct{}func (P *mymux) servehttp (w http . Responsewriter, R *http . Request) {if r.url . Path = = {Hello (w, R) return } http . NotFound (W, R) return }func Hello (w http . Responsewriter, R *http . Request) {fmt. Fprintln (W, "Hello World" )}func main () {mux: = &mymux{} http . Listenandserve ( ": 3030" , MUX)}
In this route, all of the paths you access to the site call the Hello function, which is dead, and does not implement the allocation handler function, but can also be used as an example of implementing routing.