86 lines
3.6 KiB
Markdown
86 lines
3.6 KiB
Markdown
# 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
|
|
|
|
- [Directory](preface.md)
|
|
- Previous section: [Build a simple web server](03.2.md)
|
|
- Next section: [Get into http package](03.4.md)
|