参考:https://studygolang.com/articles/25849?fr=sidebar
? http://blog.csdn.net/gophers
实现一个最简短的hello world服务器
package main
import "net/http"
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(`hello world`))
})
http.ListenAndServe(":3000", nil)
}
http.ListenAndServe()到底做了什么?
http.ListenAndServe() 用到的所有依赖都在Go 源码中的/src/pkg/net/http/server.go 文件中,我们可以看到它的定义:
ListenAndServe 来监听TCP网络地址(srv.Addr ),然后调用Serve 来处理传入的请求;
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()并返回,继续来看Server.Serve() :
func (srv *Server) Serve(l net.Listener) error {
if fn := testHookServerServe; fn != nil {
fn(srv, l)
}
origListener := l
l = &onceCloseListener{Listener: l}
defer l.Close()
if err := srv.setupHTTP2_Serve(); err != nil {
return err
}
if !srv.trackListener(&l, true) {
return ErrServerClosed
}
defer srv.trackListener(&l, false)
baseCtx := context.Background()
if srv.BaseContext != nil {
baseCtx = srv.BaseContext(origListener)
if baseCtx == nil {
panic("BaseContext returned a nil context")
}
}
var tempDelay time.Duration
ctx := context.WithValue(baseCtx, ServerContextKey, srv)
for {
rw, err := l.Accept()
if err != nil {
select {
case <-srv.getDoneChan():
return ErrServerClosed
default:
}
if ne, ok := err.(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", err, tempDelay)
time.Sleep(tempDelay)
continue
}
return err
}
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, runHooks)
go c.serve(connCtx)
}
}
Server.Serve() 的整个逻辑大概是:首先创建一个上下文对象,然后调用Listener 的Accept() 等待新的连接建立;一旦有新的连接建立,则调用Server 的newConn() 创建新的连接对象 ,并将连接的状态标志为StateNew ,然后开启一个新的goroutine 处理连接请求。继续看一下conn.Serve() 方法:
func (c *conn) serve(ctx context.Context) {
c.remoteAddr = c.rwc.RemoteAddr().String()
ctx = context.WithValue(ctx, LocalAddrContextKey, c.rwc.LocalAddr())
for {
w, err := c.readRequest(ctx)
if c.r.remain != c.server.initialReadLimitSize() {
c.setState(c.rwc, StateActive, runHooks)
}
......
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, runHooks)
c.curReq.Store((*response)(nil))
......
c.rwc.SetReadDeadline(time.Time{})
}
}
其中最关键的一行代码为serverHandler{c.server}.ServeHTTP(w, w.req) ,可以继续看一下serverHandler :
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)
}
这里的sh.srv.Handler 就是最初在http.ListenAndServe() 中传入的Handler 对象,也就是我们自定义的ServeMux 对象。如果该Handler 对象为nil ,则会使用默认的DefaultServeMux ,最后调用ServeMux 的ServeHTTP() 方法匹配当前路由对应的handler 方法。
ServeMux 是一个HTTP请求多路复用器,它将每个传入请求的URL与注册模式列表进行匹配,并调用与这个URL最匹配的模式的处理程序。
type ServeMux struct {
mu sync.RWMutex
m map[string]muxEntry
es []muxEntry
hosts bool
}
func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) {
if r.Method == "CONNECT" {
if u, ok := mux.redirectToPathSlash(r.URL.Host, r.URL.Path, r.URL); ok {
return RedirectHandler(u.String(), StatusMovedPermanently), u.Path
}
return mux.handler(r.Host, r.URL.Path)
}
host := stripHostPort(r.Host)
path := cleanPath(r.URL.Path)
if u, ok := mux.redirectToPathSlash(host, path, r.URL); ok {
return RedirectHandler(u.String(), StatusMovedPermanently), u.Path
}
if path != r.URL.Path {
_, pattern = mux.handler(host, path)
url := *r.URL
url.Path = path
return RedirectHandler(url.String(), StatusMovedPermanently), pattern
}
return mux.handler(host, r.URL.Path)
}
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)
}
if h == nil {
h, pattern = NotFoundHandler(), ""
}
return
}
func (mux *ServeMux) match(path string) (h Handler, pattern string) {
v, ok := mux.m[path]
if ok {
return v.h, v.pattern
}
for _, e := range mux.es {
if strings.HasPrefix(path, e.pattern) {
return e.h, e.pattern
}
}
return nil, ""
}
ServeMux 的Handler 方法就是根据url 调用指定handler 方法,handler 方法的作用是调用match 匹配路由。在 match 方法里我们看到之前提到的 map[string]muxEntry 和 []muxEntry ,在 map[string]muxEntry 中查找是否有对应的路由规则存在;如果没有匹配的路由规则,则会进行近似匹配。
ServeMux 的Handler 方法中找到要执行的handler 之后,就调用handler 的serveHTTP 方法。
|