您的位置:首页 > 理论基础 > 计算机网络

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

2017-11-08 19:32 429 查看
在没文档指导下阅读源码比较麻烦。

我们知道,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编程,博大精深,知其原理不易,熟用之更加不易。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: