开启一个HTTP服务,有以下两种方式:

  1. // 方式一:http.HandleFunc
  2. func main() {
  3. http.HandleFunc("/", test)
  4. http.ListenAndServe(":8090", nil)
  5. }
  6. func test(w http.ResponseWriter, r *http.Request) {}
  7. // 方式二:http.Handle
  8. type indexHandler struct {}
  9. func (ih *indexHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  10. // dosomething
  11. }
  12. func main() {
  13. http.Handle("/", &indexHandler{})
  14. http.ListenAndServe(":8001", nil)
  15. }

路由注册

HandleFunc

注意:
http.HandleFunc是一个函数, http.HanderFunc是一个类型声明。(名称相差一个字母r)。
http.Handle是一个函数,Hander是一个接口。(名称相差一个字母r)
HeadleFunc函数源码如下。
image.png
image.png
由上图可见,HandleFunc会使用HeadlerFunc类型转化的方式,使得传入的参数handler实现了ServeHTTP的方法。
image.png

Handle

image.png

  1. func (mux *ServeMux) Handle(pattern string, handler Handler) {
  2. mux.mu.Lock()
  3. defer mux.mu.Unlock()
  4. // 隐藏了一些参数验证的代码
  5. e := muxEntry{h: handler, pattern: pattern}
  6. mux.m[pattern] = e
  7. if pattern[len(pattern)-1] == '/' {
  8. // 根据url的长度,查找新的路由方法应该插入到那里
  9. mux.es = appendSorted(mux.es, e)
  10. }
  11. if pattern[0] != '/' {
  12. mux.hosts = true
  13. }
  14. }

image.png

服务监听

源码速览

image.png

  1. func (srv *Server) Serve(l net.Listener) error {
  2. for {
  3. rw, err := l.Accept()
  4. if err != nil {
  5. ...
  6. }
  7. c := srv.newConn(rw)
  8. c.setState(c.rwc, StateNew) // before Serve can return
  9. go c.serve(connCtx) // 开启一个线程处理请求,但是一个tcp连接只能同一时间只能处理一个请求
  10. }
  11. }
  12. func (c *conn) serve(ctx context.Context) {
  13. // HTTP/1.x from here on.
  14. for {
  15. w, err := c.readRequest(ctx)
  16. if err != nil {
  17. ...
  18. }
  19. serverHandler{c.server}.ServeHTTP(w, w.req)
  20. ...
  21. }
  22. }
  23. func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) {
  24. // http.ListenAndServe(":8001", nil),所有handle为空
  25. if handler == nil {
  26. handler = DefaultServeMux // 使用默认路由,也就是http.HandleFunc注册的路由
  27. }
  28. handler.ServeHTTP(rw, req)
  29. }

流程分析

image.png
1 实例化Server
2 调用Server的ListenAndServe()
3 调用net.Listen(“tcp”, addr)监听端口
4 启动一个for循环,在循环体中Accept请求
5 对每个请求实例化一个Conn,并且开启一个goroutine为这个请求进行服务go c.serve()
6 读取每个请求的内容w, err := c.readRequest()
7 判断handler是否为空,如果没有设置handler(这个例子就没有设置handler),handler就设置为DefaultServeMux
8 调用handler的ServeHttp
9 在这个例子中,下面就进入到DefaultServeMux.ServeHttp
10 根据request选择handler,并且进入到这个handler的ServeHTTP
mux.handler(r).ServeHTTP(w, r)
11 选择handler:
A 判断是否有路由能满足这个request(循环遍历ServeMux的muxEntry)
B 如果有路由满足,调用这个路由handler的ServeHTTP
C 如果没有路由满足,调用NotFoundHandler的ServeHTTP

路由匹配

image.png
当注册路由 /abc/时,会默认注册/abc,若/abc已经存在则会被覆盖。此外/abc/可以匹配到/adc/1。
但/abc不能匹配/abc/。
前文有说,路由注册的时候,mux.es中会根据url长度进行存储,所以,此处匹配时,会优先匹配到最长的字符串。
例如: /abc/1/2 会匹配到 /abc/1/ 而不是 /abc/

http问题
1、不能匹配路径参数
2、 不支持正则路由