Go 跟随源码分析http包下HTTP服务器请求过程

go语言可以用几行代码搭建一个http服务器,例如:

package main

import (
	"fmt"
	"net/http"
)

func SayHello(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "Hello World!")
}

func main() {
	http.HandleFunc("/", SayHello)
	http.ListenAndServe(":8080", nil)
}

也可以自定义多路复用器和http.Server搭建一个http服务器:

package main

import (
	"fmt"
	"net/http"
)

func SayHello(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "Hello World!")
}


func main() {
    // 创建一个多路复用器
	mux := http.NewServeMux()
    // 绑定pattern和Handler函数
	mux.HandleFunc("/", SayHello)
    // 创建server 绑定多路复用器
    server := http.Server{
		Addr:              ":8080",
		Handler:           mux,
		TLSConfig:         nil,
		ReadTimeout:       0,
		ReadHeaderTimeout: 0,
		WriteTimeout:      0,
		IdleTimeout:       0,
		MaxHeaderBytes:    0,
		TLSNextProto:      nil,
		ConnState:         nil,
		ErrorLog:          nil,
		BaseContext:       nil,
		ConnContext:       nil,
	}
    // 开始监听
	server.ListenAndServe()
}

搭建服务器的准备

从上面的代码可以看到go语言的http服务器有三个必需对象:handler函数(SayHello)、多路复用器(mux)和httpServer(server)。

先来看看从handler函数到服务器启动的过程,首先创建一个和http.HandlerFunc签名相同的handler函数SayHello,也可以是实现http.Handler接口的类型的对象。然后创建一个多路复用器,mux := http.NewServeMux()。

再调用mux.HandleFunc("/", SayHello)函数绑定路由和handler函数的关系。mux.HandleFunc("/", SayHello)后面做了2件事。1,把函数SayHello转成HandlerFunc类型;2,调用mux.Handle(pattern string, handler Handler)方法把"/"和SayHello存储到mux内部的map对象和切片对象中。

mux.HandleFunc("/", SayHello)方法的源码

// HandleFunc registers the handler function for the given pattern.
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
	if handler == nil {
		panic("http: nil handler")
	}
	mux.Handle(pattern, HandlerFunc(handler))
}

mux.Handle(pattern, HandlerFunc(handler))的源码

// Handle registers the handler for the given pattern.
// If a handler already exists for pattern, Handle panics.
func (mux *ServeMux) Handle(pattern string, handler Handler) {
	mux.mu.Lock()
	defer mux.mu.Unlock()

	if pattern == "" {
		panic("http: invalid pattern")
	}
	if handler == nil {
		panic("http: nil handler")
	}
	if _, exist := mux.m[pattern]; exist {
		panic("http: multiple registrations for " + pattern)
	}

	if mux.m == nil {
		mux.m = make(map[string]muxEntry)
	}
	e := muxEntry{h: handler, pattern: pattern}
	mux.m[pattern] = e
	if pattern[len(pattern)-1] == '/' {
		mux.es = appendSorted(mux.es, e)
	}

	if pattern[0] != '/' {
		mux.hosts = true
	}
}

 然后创建相当于套接字功能的server对象,初始化server的Handler属性为mux,最后调用server.ListenAndServe()就完成了http服务器的最简单的搭建。

服务器处理请求的过程

server.ListenAndServe()方法创建了一个套接字对象,并调用server.Serve(l net.Listener) 方法监听该套接字。

// ListenAndServe always returns a non-nil error. After Shutdown or Close,
// the returned error is ErrServerClosed.
func (srv *Server) ListenAndServe() error {
	if srv.shuttingDown() {
		return ErrServerClosed
	}
	addr := srv.Addr
	if addr == "" {
		addr = ":http"
	}
	ln, err := net.Listen("tcp", addr)
	if err != nil {
		return err
	}
	return srv.Serve(ln)
}

在server.Serve(l net.Listener)方法内部有个死循环一直监听套接字,一旦有数据被接收就会创建一个连接c,并调用创建一个 serve(ctx context.Context)函数协程。

// Serve always returns a non-nil error and closes l.
// After Shutdown or Close, the returned error is ErrServerClosed.
func (srv *Server) Serve(l net.Listener) error {
	// 代码太长  省略
	for {
		rw, e := l.Accept()
		if e != nil {
			select {
			case <-srv.getDoneChan():
				return ErrServerClosed
			default:
			}
			if ne, ok := e.(net.Error); ok && ne.Temporary() {
				if tempDelay == 0 {
					tempDelay = 5 * time.Millisecond
				} else {
					tempDelay *= 2
				}
				if max := 1 * time.Second; tempDelay > max {
					tempDelay = max
				}
				srv.logf("http: Accept error: %v; retrying in %v", e, tempDelay)
				time.Sleep(tempDelay)
				continue
			}
			return e
		}
		connCtx := ctx
		if cc := srv.ConnContext; cc != nil {
			connCtx = cc(connCtx, rw)
			if connCtx == nil {
				panic("ConnContext returned nil")
			}
		}
		tempDelay = 0
		c := srv.newConn(rw)
		c.setState(c.rwc, StateNew) // before Serve can return
		go c.serve(connCtx)
	}
}

在c.serve(ctx context.Context)方法中调用了serverHandler{c.server}.ServeHTTP(w, w.req),这里把c.server即上面文章前面搭建服务器的准备中介绍的相当于套接字功能的server对象,serverHandler类型实现了Handler接口。

// Serve a new connection.
func (c *conn) serve(ctx context.Context) {
	    // 源码太长  省略掉其他部分
		serverHandler{c.server}.ServeHTTP(w, w.req)
        // 源码太长  省略掉其他部分

}

看一下serverHandler.ServeHTTP(w,w.req)方法源码,handler := sh.srv.Handler,handler其实是server.Handler,还记得在创建server对象时初始化的Handler属性值吗?就是mux(多路复用器),这个方法最终调用的是mux.ServeHTTP(rw, req)。

// serverHandler delegates to either the server's Handler or
// DefaultServeMux and also handles "OPTIONS *" requests.
type serverHandler struct {
	srv *Server
}

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)
}

在mux.ServeHTTP(rw, req)内部调用了mux.Handler(r)方法得到handler函数对象,并执行这个函数。

// ServeHTTP dispatches the request to the handler whose
// pattern most closely matches the request 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)
}

至此一个请求的基本过程就完成了,但还有两点要解释。1,mux.Handler(r)根据request请求返回handler函数时调用了mux.handler(host, r.URL.Path),在mux.handler(host, r.URL.Path)内部又调用了mux.match(path)方法,最终这个match(path string) (h Handler, pattern string)方法才是根据请求url查询handler函数的重点。

// If there is no registered handler that applies to the request,
// Handler returns a ``page not found'' handler and an empty pattern.
func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) {

   // 代码太长  省略
	return mux.handler(host, r.URL.Path)
}

// handler is the main implementation of Handler.
// The path is known to be in canonical form, except for CONNECT methods.
func (mux *ServeMux) handler(host, path string) (h Handler, pattern string) {
	mux.mu.RLock()
	defer mux.mu.RUnlock()

	// Host-specific pattern takes precedence over generic ones
	if mux.hosts {
		h, pattern = mux.match(host + path)
	}
	if h == nil {
		h, pattern = mux.match(path)
	}
	if h == nil {
		h, pattern = NotFoundHandler(), ""
	}
	return
}

match(path string) (h Handler, pattern string)方法是从多路复用器mux内部map对象和切片对象寻找相同的请求路径或者长得像的请求路径,并返回handler函数,这里的请求路径和handler函数就是上文调用mux.HandleFunc("/", SayHello)绑定的请求路径和handler函数。

func (mux *ServeMux) match(path string) (h Handler, pattern string) {
	// Check for exact match first.
	v, ok := mux.m[path]
	if ok {
		return v.h, v.pattern
	}

	// Check for longest valid match.  mux.es contains all patterns
	// that end in / sorted from longest to shortest.
	for _, e := range mux.es {
		if strings.HasPrefix(path, e.pattern) {
			return e.h, e.pattern
		}
	}
	return nil, ""
}

2,由于上文中我使用的是mux.HandleFunc("/", SayHello)绑定请求路径和handler函数,所以调用mux.Handler(r)返回的其实是一个http.HandlerFunc类型的函数,并且http.HandlerFunc也实现了Handler接口,可以看到http.HandlerFunc的ServeHTTP(w ResponseWriter, r *Request)方法实现的是调用自身,即绑定的handler函数。

func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
	f(w, r)
}

 参考一下思维导图


附本文参考书籍:

参考文献:https://github.com/ffhelicopter/Go42/blob/master/content/42_36_http.md

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值