Go的http包详解

前面文章我们已经实现了一个简单的Web服务。现在我们详细解剖http包,分析内部实现的细节。

1.1 http包中重要的类型和接口:

  • server:HTTP服务器,定义监听的地址、端口,处理器等信息。
  • conn:用户每次请求的链接。
  • response:返回给用户的信息。
  • request:用户请求的信息。
  • Handler: 处理器,用户请求到来时,服务器的处理逻辑,它是一个包含ServeHTTP方法的接口。

1.2 http包的运行流程:

image.png

2.1 http包如何实现高并发

http包中,server每接收一个用户请求,都会生成一个conn链接,并生成一个goroutines来处理对应的conn。所以每个请求都是独立的,相互不阻塞的。

c := srv.newConn(rw)
c.setState(c.rwc, StateNew) 
go c.serve(ctx)

2.2 处理器(Handler)和多路复用器(ServeMux)

前一小节我们使用ListenAndServe启动服务,第二个参数是一个处理器。但是我们传入的是一个nil,http是如何工作的呢?

func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) {
    handler := sh.srv.Handler
    if handler == nil {
        handler = DefaultServeMux
    }
    if req.RequestURI == "*" && req.Method == "OPTIONS" {
        handler = globalOptionsHandler{}
    }
    handler.ServeHTTP(rw, req)
}

所以当我们的处理器为空时,http包会默认帮我们生成一个DefaultServeMux处理器。
不是说第二个参数是处理器吗,但是DefaultServeMux是一个ServeMux结构的实例, 是一个多路复用器。这是怎么回事?
其实处理器是一个拥有ServeHTTP方法的接口,只要实现了这个方法,它就是一个处理器。所以DefaultServeMux不仅是一个多路复用器,而且还是一个处理器。只是这个处理器比较特殊,它只是根据请求的URL将请求分配到对应的处理器。

func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) {
    if r.RequestURI == "*" {
        if r.ProtoAtLeast(1, 1) {
            w.Header().Set("Connection", "close")
        }
        w.WriteHeader(StatusBadRequest)
        return
    }
    h, _ := mux.Handler(r)
    h.ServeHTTP(w, r)
}

我们可以自定义自己的处理器

package main

import (
    "fmt"
    "net/http"
)

// 定一个自定义的Handler的结构
type MyHanlder struct{}

// 为MyHanlder结构实现Hanlder接口的ServeHTTP的方法,此时MyHandler将是一个处理器(Handler)
func (h MyHanlder) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "This my handler")
}

func main() {
    // 实例化MyHandler
    hanlder := MyHanlder{}

    //启动服务器并监听8000/tcp端口
    err := http.ListenAndServe(":8000", hanlder)
    if err != nil {
        fmt.Println(err)
    }
}

2.3 ServeMux和DefaultServeMux

前面我们说DefaultServeMux是一个ServeMux的实例,它不仅是一个多路复用器,而且是一个处理器。多路复用器具有路由功能,可以根据请求的URL将请求传递给对应的处理器处理。看下http包中默认的多路复用器是怎么实现的:

type ServeMux struct {
    mu    sync.RWMutex  //锁,应为请求是并发处理的,所以这里需要锁机制
    m     map[string]muxEntry //路由规则的map,一个string对应一个muxEntry
    es    []muxEntry 
    hosts bool       
}

type muxEntry struct {
    h       Handler    //string对应的处理器
    pattern string     //匹配的字符串
}

默认的多路复用器是根据请求的URL与存储的map做匹配,匹配到了就返回对应的handler,然后调用该handler的ServeHTTP方法来处理请求。

func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) {
    if r.RequestURI == "*" {
        if r.ProtoAtLeast(1, 1) {
            w.Header().Set("Connection", "close")
        }
        w.WriteHeader(StatusBadRequest)
        return
    }
    h, _ := mux.Handler(r)
    h.ServeHTTP(w, r)
}

func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) {

    if r.Method == "CONNECT" {
        if u, ok := mux.redirectToPathSlash(r.URL.Host, r.URL.Path, r.URL); ok {
            return RedirectHandler(u.String(), StatusMovedPermanently), u.Path
        }

        return mux.handler(r.Host, r.URL.Path)
    }

    host := stripHostPort(r.Host)
    path := cleanPath(r.URL.Path)

    if u, ok := mux.redirectToPathSlash(host, path, r.URL); ok {
        return RedirectHandler(u.String(), StatusMovedPermanently), u.Path
    }

    if path != r.URL.Path {
        _, pattern = mux.handler(host, path)
        url := *r.URL
        url.Path = path
        return RedirectHandler(url.String(), StatusMovedPermanently), pattern
    }

    return mux.handler(host, r.URL.Path)
}

func (mux *ServeMux) handler(host, path string) (h Handler, pattern string) {
    mux.mu.RLock()
    defer mux.mu.RUnlock()

    if mux.hosts {
        h, pattern = mux.match(host + path)
    }
    if h == nil {
        h, pattern = mux.match(path)
    }
    if h == nil {
        h, pattern = NotFoundHandler(), ""
    }
    return
}

我们也可以根据接口定义实现自己简单的路由器

package main

import (
    "fmt"
    "net/http"
)

// 自定义一个多路复用器的结构
type MyMux struct{}

// 实现ServeHTTP方法
func (m MyMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    // 判断URL并转到对应的handler处理
    if r.URL.Path == "/index" {
        IndexHandler(w, r)
        return
    }

    http.NotFound(w, r)
    return
}

func IndexHandler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "This is index page")
}

func main() {
    // 实例化一个自定义的路由器
    mux := MyMux{}
    err := http.ListenAndServe(":8000", mux)
    if err != nil {
        fmt.Println(err)
    }
}

代码传送门

你可能感兴趣的:(Go的http包详解)