上一章介绍了 Socket 的使用,主要提到了 TCP 以及 UDP,它们是属于传输层的协议,今天我们将介绍一个非常流出的应用层协议--HTTP。
为什么是 HTTP
HTTP 是建立在 TCP 之上的 超文本传输协议,是万维网的数据通信基础。
传统的 HTTP 协议是基于 Request/Response 模式的,一般一次请求会新建一个连接,响应结束会关闭连接。这对于早起服务端贫瘠的资源非常有意义,使其能够尽快释放连接资源,服务更多的用户。
HTTP 能够流行起来,最主要一点还是因为它对于用户而言,使用起来简单方便。
Go 语言的标准库提供了 HTTP 完整的支持,这点比很多语言好太多,你可以不使用任何第三方库或者框架就能轻松写出一个的 HTTP Server 或者 HTTP Client。
HTTP Server
下面我们就来看一个最简单的例子。
// http/server/example01.go
package main
import (
"fmt"
"log"
"net/http"
"time"
)
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w,
"Hello %s, current time is %s \n",
r.URL.Query().Get("name"),
time.Now().Format(time.RFC3339))
})
log.Fatal(http.ListenAndServe(":3000", nil))
}
本示例中我主要使用 Go 的 http
包新建并在 3000 端口启动一个 HTTP Server,然手我向这个 Server 注册一个 path 为 /
的处理函数。
处理函数逻辑为读取 HTTP Request 中的 name
Query 参数,然后向 HTTP Response 的 Body 打印输出获取的 name
和当前时间。运行效果如下:
$ curl http://localhost:3000?name=songjiayang
Hello songjiayang, current time is 2020-08-01T21:57:39+08:00
代码说明:
ListenAndServe(addr string, handler Handler) error
该方法可以新建和启动一个 HTTP Server,它主要包括两个参数,第一个是监听地址,第二个是 http.Handler
。 如果 handler 为 nil, 那么会使用 http.DefaultServeMux 这个 默认 Handler。
HandleFunc(pattern string, handler func(ResponseWriter, *Request))
该方法就是向 http.DefaultServeMux 这个 默认 Handler 注册对应的处理逻辑,而默认的 DefaultServeMux 内部其实是使用一个 map 来存储这些 path 的注册的,不支持正则。
配置 Http Server
上面的例子中我们使用 http.ListenAndServe
直接完成了 Server 的创建和监听,但是如果我们想做一些配置,例如 读写超时时间、TLS ,那么就需要将 Server 的创建和 Listen 分开,代码如下:
// http/server/example02.go
package main
import (
"fmt"
"log"
"net/http"
"time"
)
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w,
"Hello %s, current time is %s \n",
r.URL.Query().Get("name"),
time.Now().Format(time.RFC3339))
})
server := &http.Server{
Addr: ":3000",
Handler: nil,
TLSConfig: nil,
ReadTimeout: 0,
ReadHeaderTimeout: 0,
WriteTimeout: 0,
IdleTimeout: 0,
// 其它字段 ..
}
log.Fatal(server.ListenAndServe())
}
字段解读:
Handler: 具体的 handler,如果为空则使用默认的 http.DefaultServeMux
TLSConfig: TLS 相关的配置,如果不为空,那么使用 TLS 监听
ReadTimeout: 整个 http request 的读超时时间,包括 header 和 body,如果为 0 表示永远不超时
ReadHeaderTimeout: http request header 读取超时时间设置,如果为 0 表示永远不超时
WriteTimeout: http response 写超时时间,如果为 0 表示永远不超时
IdleTimeout: keep alive 的 http 连接空闲超时时间,如果为空 0 表示永远不超时
自定义 http.Handler
前面的例子我们使用的都是 http.DefaultServeMux
这个 http 包默认的 Handler,那么我们如何实现一个属于自己的 Handler 呢?
http 包中的 Handler 是一个接口,定义如下:
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}
所以只要实现该接口即可,下面我们就编写一个自己的 Handler 来重写上面的代码逻辑:
// http/server/example03.go
package main
import (
"fmt"
"log"
"net/http"
"sync"
"time"
)
func main() {
handler := NewMyHandler()
handler.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w,
"Hello %s, current time is %s \n",
r.URL.Query().Get("name"),
time.Now().Format(time.RFC3339))
})
handler.HandleFunc("/ping", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "Pong\n")
})
log.Fatal(http.ListenAndServe(":3000", handler))
}
type MyHandler struct {
mux sync.RWMutex
router map[string]HandFunc
}
type HandFunc func(w http.ResponseWriter, r *http.Request)
func NewMyHandler() *MyHandler {
return &MyHandler{
router: make(map[string]HandFunc, 8),
}
}
func (h *MyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
h.mux.RLock()
hf, ok := h.router[r.URL.Path]
h.mux.RUnlock()
// if not found
if !ok {
http.NotFound(w, r)
return
}
hf(w, r)
}
func (h *MyHandler) HandleFunc(pattern string, hf HandFunc) {
h.mux.Lock()
h.router[pattern] = hf
h.mux.Unlock()
}
代码运行结果如下:
$ curl http://localhost:3000?name=songjiayang
Hello songjiayang, current time is 2020-08-01T22:59:28+08:00
curl http://localhost:3000/ping
Pong
HTTP Client