Skip to content

Latest commit

 

History

History
85 lines (60 loc) · 3.58 KB

File metadata and controls

85 lines (60 loc) · 3.58 KB

3.3 How Go works with web

We learned to use net/http package to build a simple web server in previous section, but all the working principles are the same as we talked in first section of this chapter.

Some concepts in web working principles

Request: request data from users, including POST, GET, Cookie and URL.

Response: response data from server to clients.

Conn: connections between clients and servers.

Handler: logic of handle request and produce response.

http package operating mechanism

The following picture shows that work flow of Go's web server.

Figure 3.9 http work flow

  1. Create listening socket, listen to a port and wait for clients.
  2. Accept requests from clients.
  3. Handle requests, read HTTP header, if it uses POST method, also need to read data in message body and give them to handlers. Finally, socket returns response data to clients.

Once we know the answers of three following questions, it's easy to know how web works in Go.

  • How to listen to a port?
  • How to accept client requests?
  • How to allocate handlers?

In the previous section we saw that Go uses ListenAndServe to handle these problems: initialize a server object, call net.Listen("tcp", addr) to setup a TCP listener and listen to specific address and port.

Let's take a look at http package's source code.

//Build version go1.1.2.
func (srv *Server) Serve(l net.Listener) error {
	defer l.Close()
	var tempDelay time.Duration // how long to sleep on accept failure
	for {
		rw, e := l.Accept()
		if e != nil {
			if ne, ok := 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
				}
				log.Printf("http: Accept error: %v; retrying in %v", e, tempDelay)
				time.Sleep(tempDelay)
				continue
			}
			return e
		}
		tempDelay = 0
		c, err := srv.newConn(rw)
		if err != nil {
			continue
		}
		go c.serve()
	}
}

How to accept client requests after listened to the port? In the source code, we can see that it calls srv.Serve(net.Listener) to handle client requests. In body of function there is a for{}, it accepts request, creates a new connection, and then starts a new goroutine, and passes request data to this goroutine: go c.serve(). This is how Go supports high concurrency, and every goroutine is independent.

Now, how to use specific functions to handle requests? conn parses request c.ReadRequest() at first, and get corresponding handler: handler := c.server.Handler which is the second argument we passed when we called ListenAndServe. Because we passed nil, so Go uses it's default handler handler = DefaultServeMux. So what is DefaultServeMux doing here? Well, this is the variable of router at this time, it calls handler functions for specific URLs. Did we setting this? Yes, we did. Remember in the first line we used http.HandleFunc("/", sayhelloName). It's like you use this function to register the router rule for "/" path. When the URL is /, router calls function sayhelloName. DefaultServeMux calls ServerHTTP to get handler function for different path, and it calls sayhelloName in this case. Finally, server writes data and response to clients.

Detailed work flow:

Figure 3.10 Work flow of handling a HTTP request

I think you should know how Go runs web servers now.

Links