简单阅读golang的net/http包和Negroni的源码

在没文档指导下阅读源码比较麻烦。


我们知道,golang比起c/c++来说,已经内建了http服务的功能,而且因为golang的特性,性能不低。

下面通过阅读net/http的部分源码来看看它是怎么工作的:

func ListenAndServe(addr string, handler Handler) error {
	server := &Server{Addr: addr, Handler: handler}
	return server.ListenAndServe()
}

从ListenAndServe方法开始,这时直接调用http.ListenAndServe(addr,handler) ,它实际上是以自己接受到的参数来构造一个Server struct,然后再调用这个Server struct的ListenAndServe方法。这个Handler是用来决定使用的处理request的Handler。

func (srv *Server) ListenAndServe() error {
	addr := srv.Addr
	if addr == "" {
		addr = ":http"
	}
	ln, err := net.Listen("tcp", addr)
	if err != nil {
		return err
	}
	return srv.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)})
}

这个就是在用更底层的方法开始监听端口了,反回了srv.Serve(...)

func (srv *Server) Serve(l net.Listener) error {
	defer l.Close()
	if fn := testHookServerServe; fn != nil {
		fn(srv, l)
	}
	var tempDelay time.Duration // how long to sleep on accept failure

	if err := srv.setupHTTP2_Serve(); err != nil {
		return err
	}

	srv.trackListener(l, true)
	defer srv.trackListener(l, false)

	baseCtx := context.Background() // base is always background, per Issue 16220
	ctx := context.WithValue(baseCtx, ServerContextKey, srv)
	ctx = context.WithValue(ctx, LocalAddrContextKey, l.Addr())
	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
		}
		tempDelay = 0
		c := srv.newConn(rw)
		c.setState(c.rwc, StateNew) // before Serve can return
		go c.serve(ctx)
	}
}

这里就是写一些上下文信息,然后是一个循环,等待客户端发来信息然后Accept,这个循环中大部分代码是在处理出错后的问题,暂时忽略,如果成功Accept,就新创建一个连接来处理它,注意循环最后的
go c.serve(ctx)

这是利用goroutine来实现并发,执行完这句会有一个goroutine来执行c.serve方法,然后循环不会被阻塞,继续接收信息。


再来看看默认的Handler的定义:

type ServeMux struct {
	mu    sync.RWMutex
	m     map[string]muxEntry
	hosts bool // whether any patterns contain hostnames
}

它是怎么进行路由的?

func (mux *ServeMux) match(path string) (h Handler, pattern string) {
	var n = 0
	for k, v := range mux.m {
		if !pathMatch(k, path) {
			continue
		}
		if h == nil || len(k) > n {
			n = len(k)
			h = v.h
			pattern = v.pattern
		}
	}
	return
}
上面这是默认的ServeMux用来匹配请求的match方法,它会找出和path匹配程度最高(就是前缀匹配最长)的一条路由记录,然后我们看看关键的pathMatch方法:

func pathMatch(pattern, path string) bool {
	if len(pattern) == 0 {
		// should not happen
		return false
	}
	n := len(pattern)
	if pattern[n-1] != '/' {
		return pattern == path
	}
	return len(path) >= n && path[0:n] == pattern
}

可以看出这个匹配方法简单,就是直接逐字匹配,并不支持正则表达式等更加高级的路由功能。

如果想要用更强的路由功能,可以用第三方的gorilla/mux来实现。



再来看看Negroni的代码,这个包为golang实现了简单的中间件(middleware)机制。

type HandlerFunc func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc)

func (h HandlerFunc) ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
	h(rw, r, next)
}

上面是一个定义,类似于http包中原生的HandlerFunc,它也有一个ServeHTTP方法,但注意,他们的参数多了一个。这样做是为了让Negroni兼容非Negroni自带的函数,也就是为这些函数加上ServeHTTP方法

type middleware struct {
	handler Handler
	next    *middleware
}

func (m middleware) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
	m.handler.ServeHTTP(rw, r, m.next.ServeHTTP)
}


中间件的定义,可以看到当一个中间件调用ServeHTTP方法时,实际上是Handler起作用。将会一层一层调用下去。


func Wrap(handler http.Handler) Handler {
	return HandlerFunc(func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
		handler.ServeHTTP(rw, r)
		next(rw, r)
	})
}

func WrapFunc(handlerFunc http.HandlerFunc) Handler {
	return HandlerFunc(func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
		handlerFunc(rw, r)
		next(rw, r)
	})
}

这两个函数很有意思,Negroni可以直接用http.Handler和http.HandlerFunc来作为中间件,是怎么实现的?上面就是关键步骤,上面两个

Warp方法将http.Handler和http.HandlerFunc“包裹”住,构造了并返回一个有3个参数的HandlerFunc函数,这样就能放入中间件中使用了!



总结:golang编程,博大精深,知其原理不易,熟用之更加不易。

你可能感兴趣的:(简单阅读golang的net/http包和Negroni的源码)