?
Эта статья анализируется из нескольких частей следующим образомnet/http
модуль оserver
Часть исходного кода:
-
Handler
тип иHandlerFunc
Что это за тип? -
ServeMux
правильноHTTP handler
Управление регистрацией и распространением -
Server
начать процесс
1. Handler
1.1 Начните с регистрации обработчика HTTP
Родная регистрацияHTTP handler
Существуют следующие два способа письма, в чем разница между ними?
func handler(w http.ResponseWriter, r *http.Request) {}
http.HandleFunc("/some-pattern", handler)
http.Handle("/some-pattern", http.HandlerFunc(handler))
Исходный код, соответствующий двум методам, выглядит следующим образом:
func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
DefaultServeMux.HandleFunc(pattern, handler)
}
func Handle(pattern string, handler Handler) {
DefaultServeMux.Handle(pattern, handler)
}
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
if handler == nil {
panic("http: nil handler")
}
mux.Handle(pattern, HandlerFunc(handler))
}
ServeMux
Что это за тип, можно пока игнорировать, об этом будет сказано позже.
Можно найти, разница отражена вHandlerFunc(handler)
Этот оператор, один вызывается внутренне, а другой вызывается извне.
Эффект этой фразыПреобразовать обычную функцию вHandler
тип, в конечном итоге только реализованныйHandler
Объекты интерфейса могут быть зарегистрированы на HTTP-сервере для предоставления услуг для определенных путей и их поддеревьев.,
Он работает как адаптер.
Исходный код выглядит следующим образом
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}
type HandlerFunc func(ResponseWriter, *Request)
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
f(w, r)
}
Предположениеf
является функцией с правильной сигнатурой, тоHandlerFunc(f)
представляетHTTP handler
,Кроме,ServeHTTP
Вызов метода также представляет собой обработку запроса,
Время его вызова будет упомянуто позже.
1.2 Исходный код нескольких встроенных обработчиков и их использование
1.2.1 NotFoundHandler
Возвращает обработчик запроса, который возвращает для каждого запроса404 page not found
http.Handle("/some-pattern", http.NotFoundHandler())
исходный код
func NotFound(w ResponseWriter, r *Request) { Error(w, "404 page not found", StatusNotFound) }
func NotFoundHandler() Handler { return HandlerFunc(NotFound) }
1.2.2 RedirectHandler
Возвращает обработчик запроса, который перенаправляет на URL-адрес с кодом кода состояния для каждого запроса.
http.Handle("/some-pattern", http.RedirectHandler("/", 301))
Исходный код, тут одно могу сказать, функция Redirect вроде не имеет никакого аналитического значения, пропускаем
type redirectHandler struct {
url string
code int
}
func RedirectHandler(url string, code int) Handler {
return &redirectHandler{url, code}
}
func (rh *redirectHandler) ServeHTTP(w ResponseWriter, r *Request) {
Redirect(w, r, rh.url, rh.code)
}
1.2.3 StripPrefix
Отфильтруйте определенные префиксы из URL-адреса, прежде чем направлять запрос обработчику запросов, который вы указываете с помощью параметров.
http.Handle("/api/some-pattern", http.StripPrefix("/api", handler))
исходный код
func StripPrefix(prefix string, h Handler) Handler {
if prefix == "" {
return h
}
return HandlerFunc(func(w ResponseWriter, r *Request) {
if p := strings.TrimPrefix(r.URL.Path, prefix); len(p) < len(r.URL.Path) {
// 浅拷贝request
r2 := new(Request)
*r2 = *r
// r.URL是一个引用,需要再拷贝一次
r2.URL = new(url.URL)
*r2.URL = *r.URL
// 重置请求路径
r2.URL.Path = p
h.ServeHTTP(w, r2)
} else {
NotFound(w, r)
}
})
}
Есть только два пункта, чтобы понять, чтобы понять этот раздел:
-
new(T)
Выделить место в памяти для типа и вернуть указатель на эту память -
получить значение из переменной-указателя
*
Операция, вы можете получить значение исходной переменной, на которую указывает переменная-указатель
1.2.4 TimeoutHandler
Возвращает обработчик запроса с указанным ограничением по времени. Если вызов занимает больше времени, чем ограничение по времени, обработчик ответит кодом состояния запроса.503 Service Unavailable
, и воляmsg
как предмет ответа.
func handler(w http.ResponseWriter, r *http.Request) {
time.Sleep(2 * time.Second)
}
http.Handle("/some-pattern", http.TimeoutHandler(http.HandlerFunc(handler), 1 * time.Second, "Timeout"))
определение
type timeoutHandler struct {
handler Handler
body string
dt time.Duration
}
func TimeoutHandler(h Handler, dt time.Duration, msg string) Handler {
return &timeoutHandler{
handler: h,
body: msg,
dt: dt,
}
}
При возбуждении процессора запроса,ServeHTTP
Метод делает следующее (упрощает некритичный код для удобства чтения)
func (h *timeoutHandler) ServeHTTP(w ResponseWriter, r *Request) {
// 初始化一个可被取消的上下文
ctx, cancelCtx := context.WithTimeout(r.Context(), h.dt)
defer cancelCtx()
// 设置r.ctx
r = r.WithContext(ctx)
done := make(chan struct{})
tw := &timeoutWriter{
w: w,
h: make(Header),
}
// 起一个goroutine来执行原本的逻辑
go func() {
h.handler.ServeHTTP(tw, r)
close(done)
}()
// 等待一个通信
select {
// 如果没有超时正常返回
case <-done:
tw.mu.Lock()
defer tw.mu.Unlock()
dst := w.Header()
for k, vv := range tw.h {
dst[k] = vv
}
if !tw.wroteHeader {
tw.code = StatusOK
}
w.WriteHeader(tw.code)
w.Write(tw.wbuf.Bytes())
// 如果超时
case <-ctx.Done():
tw.mu.Lock()
defer tw.mu.Unlock()
w.WriteHeader(StatusServiceUnavailable)
io.WriteString(w, h.errorBody())
tw.timedOut = true
}
}
2. Мультиплексор ServeMux
ServeMux
это мультиплексор для HTTP-запросов, который сопоставляет каждый URL-адрес входящего запроса со списком зарегистрированных шаблонов и вызывает обработчик для наиболее подходящего шаблона,
Регистрация на первую частьHTTP handler
процесс на самом делеServeMux
Добавить запись во внутреннюю хеш-таблицу
2.1 Процесс регистрации
ServeMux
Следующая структура
type ServeMux struct {
// 读写锁
mu sync.RWMutex
// 管理所有注册路由哈希表
m map[string]muxEntry
// 按pattern长度降序排列的匹配列表, 记录值均以/结尾
es []muxEntry
// 是否存在hosts, 即不以'/'开头的pattern
hosts bool
}
type muxEntry struct {
h Handler
pattern string
}
var DefaultServeMux = &defaultServeMux
var defaultServeMux ServeMux
Как упоминалось выше, одна регистрация за разHTTP handler
Последний звонокDefaultServeMux.Handle(pattern, handler)
метод,
То, что делает этот метод, очень просто, это поддерживать внутреннюю хеш-таблицуm
, после исключения части кода обработки ошибок исходный код выглядит следующим образом:
func (mux *ServeMux) Handle(pattern string, handler Handler) {
mux.mu.Lock()
defer mux.mu.Unlock()
// 如果注册一个已注册的处理器,将panic
if _, exist := mux.m[pattern]; exist {
panic("http: multiple registrations for " + pattern)
}
// 注册
e := muxEntry{h: handler, pattern: pattern}
mux.m[pattern] = e
// 以斜杠结尾的pattern将存入es切片并按pattern长度降序排列
if pattern[len(pattern)-1] == '/' {
mux.es = appendSorted(mux.es, e)
}
// 不以"/"开头的模式将视作存在hosts
if pattern[0] != '/' {
mux.hosts = true
}
}
Процесс регистрации окончен, так сказатьhttp.Handle(pattern, handler)
просто просто вDefaultServeMux
Хеш-таблицаm
Регистрация осуществляется в , а раздача не начата
2.2 Распространение
ServeMux
Структура также реализуетHandler
интерфейс, так что это настоящий дистрибьютор!
func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) {
if r.RequestURI == "*" {
// 对于http协议小于1.1的处理
if r.ProtoAtLeast(1, 1) {
w.Header().Set("Connection", "close")
}
w.WriteHeader(StatusBadRequest)
return
}
// 寻找到最接近的HTTP handler
h, _ := mux.Handler(r)
h.ServeHTTP(w, r)
}
mux.Handler(r)
метод всегда будет возвращать ненулевойHTTP handler
, который содержит больше обработки особых случаев.С точки зрения изучения исходного кода, попадать в эти ветки неправильно.
При анализе учитывается наиболее доминирующий случай, поэтомуHandler
Способ уменьшает:
func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) {
// 当请求地址为/tree,但只注册了/tree/未注册/tree时,301重定向
// 此处redirectToPathSlash并没有分析价值,检测一下两者是否在mux.m哈希表中即可
if u, ok := mux.redirectToPathSlash(host, path, r.URL); ok {
return RedirectHandler(u.String(), StatusMovedPermanently), u.Path
}
return mux.handler(host, r.URL.Path)
}
Последнийhandler
являетсяHandler
Ядро реализации метода, исходный код выглядит следующим образом:
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)
}
// 如果没有匹配到任何Handler,将返回404 handler
if h == nil {
h, pattern = NotFoundHandler(), ""
}
return
}
Предположим, в это времяDefaultServeMux
Зарегистрированы две схемы:/a/
, /a/b/
,В настоящее времяDefaultServeMux
Структура
{
m: {
"/a/": { h: HandlerA, pattern: "/a/" },
"/a/b/": { h: HandlerB, pattern: "/a/b" },
},
es: [{ h: HandlerB, pattern: "/a/b" }, { h: HandlerA, pattern: "/a/" }]
}
Когда путь запроса/a/b/c
, войдет во второйif
приговор, вmatch
метод соответствия:
func (mux *ServeMux) match(path string) (h Handler, pattern string) {
// 直接匹配成功的情况
v, ok := mux.m[path]
if ok {
return v.h, v.pattern
}
// 寻找最接近的最长匹配,mux.es切片中包含了所有子树,并降序排列,因此遍历一次即可找出最接近的模式
for _, e := range mux.es {
if strings.HasPrefix(path, e.pattern) {
return e.h, e.pattern
}
}
return nil, ""
}
конечный путь/a/b/c
вернусьhandlerB
3. Server
Server
Часть конструкции выглядит следующим образом:
type Server struct {
// 监听的地址和端口
Addr string
// 所有请求都要调用的Handler
Handler Handler
// 读的最大超时时间
ReadTimeout time.Duration
// 写的最大超时时间
WriteTimeout time.Duration
// 请求头的最大长度
MaxHeaderBytes int
...
}
3.1 начиная со стартового HTTP-сервера
пример кода
http.ListenAndServe(":8001", nil)
Исходный код фактически создает экземпляр сервера, когдаhandler
заnil
, буду использоватьDefaultServerMux
по умолчаниюhandler
,
То есть упомянутый во втором разделе "мультиплексор", который также является наиболее распространенной практикой.
func ListenAndServe(addr string, handler Handler) error {
server := &Server{Addr: addr, Handler: handler}
return server.ListenAndServe()
}
существуетserver.ListenAndServe
Метод, позвонитnet.Listen("tcp", addr)
прослушивающий порт,
Но оTCP
Содержание не входит в объем анализа, перейдите непосредственно кsrv.Serve
метод
func (srv *Server) ListenAndServe() error {
addr := srv.Addr
ln, err := net.Listen("tcp", addr)
if err != nil {
return err
}
return srv.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)})
}
существуетsrv.Serve
метод вfor
В контуре сделана следующая работа:
-
перечислить
l.Accept()
Получить новое подключение, последующие действия -
будет
TCP conn
преобразовано в серверную частьHTTP conn
-
начать один
goroutine
справиться с этимHTTP conn
func (srv *Server) Serve(l net.Listener) error {
l = &onceCloseListener{Listener: l}
defer l.Close()
// 获得根context
baseCtx := context.Background()
// 返回一个在根context的基础上添加键为ServerContextKey,值为当前Server引用的context
ctx := context.WithValue(baseCtx, ServerContextKey, srv)
for {
// 接收一个请求
rw, e := l.Accept()
// 将tcp conn转换为http conn
c := srv.newConn(rw)
// 启动一个goroutine处理这个请求
go c.serve(ctx)
}
}
c.serve(ctx)
Будет проведена окончательная обработка, эта часть сложнее, на самом деле нужно только ухаживатьserverHandler{c.server}.ServeHTTP(w, w.req)
Эта линия
// Serve a new connection.
func (c *conn) serve(ctx context.Context) {
// HTTP/1.x from here on.
c.r = &connReader{conn: c}
c.bufr = newBufioReader(c.r)
c.bufw = newBufioWriterSize(checkConnErrorWriter{c}, 4<<10)
ctx, cancelCtx := context.WithCancel(ctx)
defer cancelCtx()
for {
w, err := c.readRequest(ctx)
......
serverHandler{c.server}.ServeHTTP(w, w.req)
......
}
}
В конце концов, это называетсяDefaultServeMux
в целомHTTP handler
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)
}
OVER