golang学习之net/http 源码分析

golang学习之net/http 源码分析

在理解http中,重点关注在server和client以及他们的交互流程。

流程:Client->Requests->router->handler->Response->Client

  • hander函数: 具有func(w http.ResponseWriter, r *http.Requests)签名的函数 
  • handler处理器(函数): 经过HandlerFunc结构包装的handler函数,它实现了ServeHTTP接口方法的函数。调用handler处理器的ServeHTTP方法时,即调用handler函数本身。
  • handler对象:实现了Handler接口ServeHTTP方法的结构。

建立http服务器代码 

package main

import (
    "net/http"
    "fmt"
)

func Indexhandler(w http.ResponseWriter,r *http.Request)  {
    fmt.Fprintln(w,"hello world")
}


func main() {
    http.HandleFunc("/",Indexhandler)
    http.ListenAndServe("127.0.0.1",nil)
}

 ListenAndServe函数功能 监听TCP地址addr,并且会使用handler参数调用Serve函数处理接收到的连接ListenAndServe设置监听的端口。 handler参数一般会设为nil,此时会使用DefaultServeMux(默认的路由器)。源代码如下:

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

可以看出先实现方法为:利用端口号和处理函数初始化一个 Server 结构实例,然后通过调用这个实例的 ListenAndServe() 来实现监听功能的。

 Server 结构的 ListenAndServe() 函数源代码如下:

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)})
}

 Server 结构通过调用net.Listen("tcp", addr) 在底层用 TCP 协议搭建了一个服务,然后监听我们设置的端口。这个函数将会返回一个 Listener 接口,这个接口用于建立连接,关闭连接和返回网络地址。
 

 HandleFunc函数功能HandleFunc注册一个模式pattern(URI)和对应的处理器函数handler(controller)注册到HTTP包默认的路由器-DefaultServeMux中。 HandleFunc函数源码如下:

func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
	DefaultServeMux.HandleFunc(pattern, handler)
}

这个函数的实现中,我们自己写的Indexhandler函数并没有实现ServeHTTP这个接口,但作为参数传递有函数类型的转换。因为这个函数调用了 DefaultServeMux中的HandleFunc 这个方法 ,这个方法的函数实现:

func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
	mux.Handle(pattern, HandlerFunc(handler))

在这个方法里,就可以看到类型转换 HandlerFunc(handler)的过程。 

 

 接收客户端请求代码

 Go 通过调用 Serve(net.Listener) 来接收客户端的请求。在源代码中,通过调用接口的 Accept() 函数接收客户端请求,其中变量 rw储存request信息,调用 newConn(rw) 创建一个新的连接 c ,最后单独创建一个 goroutine ,把相应的上下文信息作为参数去调用新连接的 c.serve(ctx) 函数,处理客户端的请求。

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)
    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)
    }
}

 在 c.serve(ctx) 函数中,调用 w, err := c.readRequest(ctx) ,取出分析相应的请求信息。

调用 serverHandler{c.server}.ServeHTTP(w, w.req) ,获取相应的处理函数对请求信息进行处理。

 

 

你可能感兴趣的:(服务计算)