基于go 1.13版本
在分析源码前,要知道
在 http 服务器上进行了如下工作:
-
客户端通过 TCP/IP 协议建立与服务器的 TCP 连接
-
客户端向服务器发送 HTTP 协议请求报文,请求获得服务器资源
-
服务器解析接收到的 HTTP 协议请求报文,并根据报文内容处理相关的数据,然后把请求的资源通过 HTTP 协议响应报文发送回给客户端
-
客户端与服务器断开,由客户端的浏览器解析并渲染返回的 HTML 文档,并把网页显示在屏幕上
源码分析
在pkgdoc的标准库文档中,有http server的demo
http.Handle("/foo", fooHandler)
http.HandleFunc("/bar", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path))
})
log.Fatal(http.ListenAndServe(":8080", nil))
http.ListenAndServe(":8080", nil)入口开始看源码调用链
监听主机的8080端口,nil表示不使用自定义的路由handler,即使用默认的DefaultServeMux(一个使用hashmap的简单路由,gin的话是自己实现了一个路由树的路由handler)
// ListenAndServe always returns a non-nil error.
func ListenAndServe(addr string, handler Handler) error {
server := &Server{Addr: addr, Handler: handler}
return server.ListenAndServe()//关键行
}
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) //关键行
}
type Listener interface {
// Accept waits for and returns the next connection to the listener.
Accept() (Conn, error)
// Close closes the listener.
// Any blocked Accept operations will be unblocked and return errors.
Close() error
// Addr returns the listener's network address.
Addr() Addr
}
其server.ListenAndServe()通过调研net包的net.Listen("tcp", addr)返回一个listener接口,这个listener接口定义了Accetp,Close,Addr方法,分别用于建立新连接,关闭连接和返回连接对应的网络地址。
func (srv *Server) Serve(l net.Listener) error {
if fn := testHookServerServe; fn != nil {
fn(srv, l) // call hook with unwrapped listener
}
origListener := l
l = &onceCloseListener{Listener: l}
defer l.Close()
// 去掉不关键代码....
ctx := context.WithValue(baseCtx, ServerContextKey, srv)
for {
rw, e := l.Accept()
// 去掉不关键代码....
c := srv.newConn(rw)
c.setState(c.rwc, StateNew) // before Serve can return
go c.serve(ctx)
}
}
和正常的tcp编程一样,对于每一个listener返回的Conn
,都开辟一个新的协程去处理他。然后当前协程继续执行Accept()
。
对于c.serve(ctx)函数内部,关键操作有两个:
-
w, err := c.readRequest(ctx),解析客户端请求(只解析http包的请求行和请求头。payload在用户传入的handler使用到时,才解析。如调用
func (r *Request) FormValue(key string) string
) -
serverHandler{c.server}.ServeHTTP(w, w.req)
使用c.server构造serverHandler后调用ServeHTTP,默认的c.server就是DefaultServeMux,根据path调用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)
}
// Serve a new connection.
func (c *conn) serve(ctx context.Context) {
// 省略部分代码
// HTTP/1.x from here on.
ctx, cancelCtx := context.WithCancel(ctx)
c.cancelCtx = cancelCtx
defer cancelCtx()
c.r = &connReader{conn: c}
c.bufr = newBufioReader(c.r)
c.bufw = newBufioWriterSize(checkConnErrorWriter{c}, 4<<10)
for {
w, err := c.readRequest(ctx)
// 省略部分代码
// HTTP cannot have multiple simultaneous active requests.[*]
// Until the server replies to this request, it can't read another,
// so we might as well run the handler in this goroutine.
// [*] Not strictly true: HTTP pipelining. We could let them all process
// in parallel even if their responses need to be serialized.
// But we're not going to implement HTTP pipelining because it
// was never deployed in the wild and the answer is HTTP/2.
serverHandler{c.server}.ServeHTTP(w, w.req)
w.cancelCtx()
if c.hijacked() {
return
}
w.finishRequest()
if !w.shouldReuseConnection() {
if w.requestBodyLimitHit || w.closedRequestBodyEarly() {
c.closeWriteAndWait()
}
return
}
c.setState(c.rwc, StateIdle)
c.curReq.Store((*response)(nil))
if !w.conn.server.doKeepAlives() {
// We're in shutdown mode. We might've replied
// to the user without "Connection: close" and
// they might think they can send another
// request, but such is life with HTTP/1.1.
return
}
if d := c.server.idleTimeout(); d != 0 {
c.rwc.SetReadDeadline(time.Now().Add(d))
if _, err := c.bufr.Peek(4); err != nil {
return
}
}
c.rwc.SetReadDeadline(time.Time{})
}
}
网友评论