This is a creation in Article, where the information may have evolved or changed.
Reference
Golang Building an HTTP service
Server.go Source
HTTP
Network development, many network applications are built on the basis of the re-HTTP service. The HTTP protocol from birth to the present, development from 1.0, 1.1 to 2.0 also continue to progress. In addition to the details, the Web application that understands HTTP builds is focused on the two-side---client (clinet) and server side (server), two-side interactions come from Clinet's request, and response on the servers side. The so-called HTTP server, mainly depends on how to accept clinet request, and return to the client response.
In the process of receiving the request, the most important thing is routing ( router
), that is, implementing a Multiplexer
(Multiplexer). You can use the built-in mutilplexer
---defautservemux or customize it in go. Multiplexer
The purpose of routing is to find the processor function (handler), which will process the request and build the response.
Clinet -> Requests -> [Multiplexer(router) -> handler -> Response -> Clinet
First look at the implementation of one of the simplest HTTP servers:
// mini web serverpackage mainimport ( "fmt" "log" "net/http")func IndexHandler(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, "
The purpose of this program is to open a Web service on port 9090 of this machine, and when accessing http://localhost:9090/, the Web page will display Hello world.
We analyze this procedure and start with the main () function.
Handlefunc and Handler
The following is the definition of the Handler interface. It can be seen that it requires implementing the Servehttp method.
type Handler interface { ServeHTTP(ResponseWriter, *Request)}
Handler is used to respond to HTTP requests, and the Servehttp method writes the header and data of the reply to and ResponseWriter
then returns. As the indexhandler above us do.
However, once the servehttp call is completed, we can no longer read Request.Body
or use it ResponseWrite
.
Because of the HTTP client, the HTTP protocol version, and the process between the client and the server, we may ResponseWrite
not be able to read it immediately after the write Request.Body
. So we should read it first Request.Body
and then reply.
Note that Request
it is read-only and cannot be modified.
// http.HandleFuncfunc HandleFunc(pattern string, handler func(ResponseWriter, *Request))
Handlefunc registers the handle function with a pattern, just as we will register it with the root directory in the program above IndexHandler
"/"
. Subsequent access to the root directory will be processed using the corresponding handler. Specific how to match the pattern needs to understand Servemux.
Servemux
Servemux is a multiplexer for HTTP requests. It matches the requested URL with all of the registered pattern, and then invokes the most similar one.
Assuming that we want to access the path starting with "/images/thumbnails/", and now we have registered "/images/" and "/images/thumbnails/", then obviously the "/images/thumbnails/" corresponds to the The handle function will be processed.
Therefore, the root directory is "/"
not only matched to the "/"
URL of the path, but also to the root when there is no other pattern to match.
The implementation of Servemux is very simple, mainly based on a dictionary query.
type ServeMux struct { mu sync.RWMutex m map[string]muxEntry hosts bool }type muxEntry struct { explicit bool h Handler pattern string}
Server
Now there is only one line of code that needs to be analyzed.
log.Fatal(http.ListenAndServe(":9090", nil))
The first parameter is the port to listen on. The second parameter is to support a custom multiplexer if nil is used as the default.
The relevant functions can be found in the source code:
- Create a new Server instance and set the address and multiplexer to start listening and service.
func ListenAndServe(addr string, handler Handler) error { server := &Server{Addr: addr, Handler: handler} return server.ListenAndServe()}
- Listening port
func (srv *Server) ListenAndServe() error { addr := srv.Addr if addr == "" { addr = ":http" } ln, err := net.Listen("tcp", addr) if err != nil { return err } return srv.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)})}
The core code is ln, err := net.Listen("tcp", addr)
.
Process a new connection
Func (SRV *server) Serve (l net. Listener) Error {defer l.close () if fn: = Testhookserverserve; fn! = nil {fn (SRV, L)} var tempdelay time. Duration//How long-to-sleep on accept failure If err: = Srv.setuphttp2_serve (); Err! = Nil {return err} 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 () if E! = Nil {select { Case <-srv.getdonechan (): Return errserverclosed default:} If NE, O K: = e. (NET. ERROR); Ok && ne. Temporary () {if Tempdelay = = 0 {tempdelay = 5 * time. Millisecond} else {Tempdelay *= 2} if Max: = 1 * time. Second; Tempdelay > MAx {tempdelay = max} srv.logf ("Http:accept error:%v; Retrying in%v ", E, Tempdelay) time. Sleep (Tempdelay) Continue} return e} tempdelay = 0 c: = Srv.newconn ( RW) c.setstate (C.RWC, statenew)//Before Serve can return go C.serve (CTX)}}
Here, A For loop is opened, and a goroutine processing is established for each new connection, which embodies the high concurrency concept.
- Processing logic for each connection
This function is longer, and temporarily does not stick to the code.
The main things to do are:
- Resolving client requests
- Select Multiplexer, and if it is nil then use Defaultservemux
- Use multiplexer to locate the handler that corresponds to the request URI
- Write Responsewriter and reply to client
At this point, we understand how the HTTP service works.