Go - Анализ исходного кода http.Server

Go

?

Эта статья анализируется из нескольких частей следующим образом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

3.2 Общий процесс