net/http 是如何处理请求

153 阅读2分钟

1.概览

这篇文章是分析golang 的 net/http包,了解 server 端是如何建立 socket 并处理客户端请求的

c语言中,server端调用的系统方法(socket、bind、listen、accept):

1.int listenfd=socket(AF_INET,SOCK_STREAM,0);
2.bind(listenfd,(struct sockaddr*)&local,sizeof(local))
3.listen(listenfd,BLOCK)<0)
while(1){
// 从listen_fd的请求连接队列中取出一个请求并创建一个新的套接字,
// 这个新的套接字用来和客户端进行通信
4.int connfd=accept(listen_fd,(struct sockaddr*)&client,&len);
if(connfd<0){
continue;
}
5.int size=recv(connfd,&buf,sizeof(buf)-1,0);
}

具体介绍可以看我的这一篇文章,有完整的demo实例 blog.51cto.com/lingdandan/…

先看一下 golang 如何使用 net/http :

func main() {
   //注册路由
   http.HandleFunc("/ping", func(w http.ResponseWriter, r *http.Request) {
      w.Write([]byte("pong"))
   })

   http.ListenAndServe("127.0.0.1:8080", nil)

2.接下来是了解底层:

0.注册处理器

http.HandleFuncDefaultServeMux中注册一个handler。DefaultServeMux是go默认提供的ServeMux

func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
   DefaultServeMux.HandleFunc(pattern, handler)  《===== HandleFunc 在 DefaultServeMux 中注册处理程序函数。
}
var DefaultServeMux = &defaultServeMux
var defaultServeMux ServeMux

// ServeMux 是一个http请求的多路复用器.
type ServeMux struct {
   mu    sync.RWMutex
   m     map[string]muxEntry
   es    []muxEntry // slice of entries sorted from longest to shortest.
   hosts bool       // whether any patterns contain hostnames
}

1.创建socket

func ListenAndServe(addr string, handler Handler) error {
	server := &Server{Addr: addr, Handler: handler}
	return server.ListenAndServe()
}
func (srv *Server) ListenAndServe() error {
	// ... 省略代码
	ln, err := net.Listen("tcp", addr) 《=====创建listen_fd
	if err != nil {
		return err
	}
	return srv.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)}) 《=====这里accept
}

2.accept等待客户端链接

func (srv *Server) Serve(l net.Listener) error {
	// ... 省略代码
	for {
		rw, e := l.Accept() 《=====这里进行accept
		if e != nil {
			select {
			case <-srv.getDoneChan():
				return ErrServerClosed
			default:
			}
                        
                        // ... 省略代码
			return e
		}
		tempDelay = 0
		c := srv.newConn(rw)  《=====创建连接准备处理请求
		c.setState(c.rwc, StateNew) // before Serve can return
		go c.serve(ctx)  《=====处理请求
	}
}

3.提供新连接处理请求

func (c *conn) serve(ctx context.Context) {
	// ... 省略代码
	serverHandler{c.server}.ServeHTTP(w, w.req)  《===== 
	// ... 省略代码
}
// serverHandler 是 委托给服务器的Handler 或 DefaultServeMux
type serverHandler struct {
   srv *Server
}

func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) {
        handler := sh.srv.Handler
        if handler == nil {
            handler = DefaultServeMux  《=====使用默认的ServeMux(是一个http请求的多路复用器)
        }
        if req.RequestURI == "*" && req.Method == "OPTIONS" {
            handler = globalOptionsHandler{}
        }
        // Gin框架中用的handler是 engine.Handler(),调用 func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {}
        // net/http中默认使用的handler是 DefaultServeMux ,调用 func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) {}
        handler.ServeHTTP(rw, req)  《===== 
}
// ServeHTTP 用来响应http请求。
// ServeMux实现了该方法,其主要作用是分发http请求给相应的handler。
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) 《=====返回用于给定请求的处理程序,参考 r.Method、r.Host 和 r.URL.Path。 它总是返回一个非零处理程序。
	h.ServeHTTP(w, r)  《=====
}

4.调用实际要执行的 handler处理程序

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

总结一下整个过程:

  1. ln, err := net.Listen("tcp", addr) 做了socket、bind、listen的初始化操作
  2. rw, e := l.Accept() 进行阻塞的accept,等待客户端的到来
  3. go c.serve(ctx) 启动新的goroutine处理本次请求
  4. h, _ := mux.Handler(r) 获取注册的路由,然后拿到这个路由的handler,然后将处理结果返回给客户端