一、Go 的 HTTP 标准库 net/http
Go 已经拥有成熟的 HTTP 标准库 net/http
,即使不使用 Go 的 Web 框架如 Gin、Iris 等也可以快速的搭建起一个可以运行的 Web 服务,同时这个标准库可以对 Web 路由、静态文件、模板和cookie 等数据进行处理,事实上这些 Web 框架也都是基于 net/http
标准库来构建的。
使用 net/http
建立 Web 服务器
首先创建一个请求处理函数 sayHelloHandler
,该函数接收一个 http.ResponseWriter
和 一个请求指针 *http.Request
作为参数,通过对请求中数据的提取之后写入指定的信息到 http.ResponseWriter
中。
在 main 函数中通过 http.HandleFunc
定义一个路由并将该路由与 sayHelloHandler
函数映射,然后通过 http.ListenAndServe
监听端口。
运行 main.go 文件,一个简单的 Web 服务器就运行起来了。
package main // filename main.go //noinspection ALL import ( "fmt" "log" "net/http" "strings" ) //noinspection ALL func main(){ http.HandleFunc("/", sayHelloName) err := http.ListenAndServe(":9000", nil) if err != nil { log.Fatal("ListenAndServer: ", err) } } func sayHelloHandler(w http.ResponseWriter, r *http.Request){ r.ParseForm() // 解析参数,默认是不会解析的 fmt.Println(r.Form) // 输出 fmt.Println("path", r.URL.Path) fmt.Print("Scheme", r.URL.Scheme) fmt.Println(r.Form["url_long"]) for k, v := range r.Form{ fmt.Println("Key", k) fmt.Println("Val", strings.Join(v, "")) } fmt.Fprint(w, "Hello, Go HTTP") } 复制代码
在浏览器中输入 http://localhost:9000/
控制台打印出的详细信息
net/http
运行机制
net/http
运行流程:
- 创建 Listen Socket 监听指定端口,等待客户端请求
- Listen Socket 接收客户端请求,得到 Client Socket,服务端通过 Clent Socket 与客户端进行通信
- 处理客户端请求,首先从 Client Socket 读取 HTTP 请求的信息,包括请求头和请求体,然后交给相应的 handler 函数处理,handler 处理完成后将数据通过 Client Socket 写给客户端。
整个过程我们需要了解三个问题:
net/http
是如何监听端口的?net/http
是如何接收客户端请求的?net/http
是如何分配 handler 的?
查看源码,点击 main.go 文件中 http.ListenAndServe(":9000", nil)
的 ListenAndServe 函数,来到源码,再次点击 3222 行的 ListenAndServe,
再次点击 2968 行的 Serve 函数:
Serve(l net.Listener) 函数就是处理接收客户端的请求信息。该函数中的 for 循环首先通过 net.Listener 接收请求 rw, err := l.Accept()
,for 循环之后又创建了一个 Conn,最后单独开了一个 goroutine go c.serve(connCtx)
;用户的每一次请求都是在一个单独的 goroutine 中执行的,不会相互影响。
点击 go c.serve(connCtx)
中的 serve(connCtx)
方法,在该方法中的第 1891 行通过 c.readRequest(ctx)
来解析请求:
然后在第 1966 行,根据请求解析结果通过 serverHandler{c.server}.ServeHTTP(w, w.req)
分配了一个 handler:
点击查看 ServeHTTP 源码:
这里获取了一个 handler,调用 ListenAndServe 函数的时候传递的参数为 nil,因此这里默认分配了一个 DefaultServeMux 作为 handler。其实 DefaultServeMux 就是一个路由器,它用来匹配 URL 跳转到其响应的 handler 函数。
上述代码中 http.HandleFunc("/", sayHelloName)
就已经定义了路由规则,当请求为 /
时,路由就会转到 sayHelloName 方法,DefaultServeMux 就会调用 ServeHTTP 方法,则这个方法内部其实就是调用 sayHelloName 方法,然后将返回结果写入到 response 中,最后返回给客户端。
整个处理流程如下: