golang Http服务浅析

golang的HTTP包提供了了很强大的功能,开发人员即使不使用框架也可以很方便的进行开发。下面就简单说一下开发web应用时HTTP包都做了哪些工作。

我们在建立一个WEB应用的时候经常会这样使用:


    http.HandleFunc("/hello", func(writer http.ResponseWriter, request *http.Request) {
        writer.Write([]byte("hello"))
    })
    http.ListenAndServe(":8080",nil)

这样我们访问8080端口就会打印hello

下面从两个介绍HTTP的工作流程:
1.handler注册过程
2.服务器监听过程
3.当我们访问接口时服务器的处理过程。

一 注册过程

注册的代码是:


    http.HandleFunc("/hello", func(writer http.ResponseWriter, request *http.Request) {
        writer.Write([]byte("hello"))
    })
    

看一下http.HandleFunc函数:


var DefaultServeMux = &defaultServeMux

var defaultServeMux ServeMux


func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
    DefaultServeMux.HandleFunc(pattern, handler)  //调用ServeMux的HandleFunc函数
}

这里发现http.HandleFunc调用的是DefaultServeMux的HandleFunc函数,DefaultServeMux是ServeMux类型的变量。下面看看ServeMux。

type ServeMux struct {
    mu    sync.RWMutex
    m     map[string]muxEntry  //key是路径
    hosts bool
}

type muxEntry struct {
    h       Handler   //通过HandleFunc函数传过来的handler
    pattern string  //路径
}

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

func (mux *ServeMux) Handle(pattern string, handler Handler) {
    mux.mu.Lock()
    defer mux.mu.Unlock()
    //....
    mux.m[pattern] = muxEntry{h: handler, pattern: pattern}
    //....
}

这里HandleFunc函数调用Handle函数,然后通过mux.m[pattern] = muxEntry{h: handler, pattern: pattern}把handler存储到muxEntry然后放到ServeMux的map中。
这里可以发现HandleFunc函数中会把handler强制转换为HandlerFunc类型:HandlerFunc(handler)。
来看看HandleFunc类型。


type HandlerFunc func(ResponseWriter, *Request)


func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
    f(w, r)
}

HandlerFunc和我们传入的Handler的函数签名是一样的,只是它还有一个ServeHTTP函数,这个函数会调用我们传入的handler。可以看出HandlerFunc只是对我们传入的handler进行了包装并提供ServeHTTP来调用handler

这里总结一下注册handler的流程:
1.http.HandlerFunc调用ServeMux. HandleFunc方法。
2.ServeMux. HandleFunc首先对我们传进来的handler强制转换为HandlerFunc类型(目的是提供ServerHTTP接口来执行我们传进来的Handler),然后调用ServeMux. Handle方法。
3.ServeMux. Handle方法会把path和handler存在ServeMux结构的m字段中(使用map的形式存放)。

二.监听过程

调用http.ListenAndServe函数,看看它的代码

扫描二维码关注公众号,回复: 3416612 查看本文章

func ListenAndServe(addr string, handler Handler) error {
    server := &Server{Addr: addr, Handler: handler}
    return server.ListenAndServe()   //调用Server类型的同名方法
}

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

func (srv *Server) Serve(l net.Listener) error {
    //....
    for {
        rw, e := l.Accept() //等待请求
        //...
    }
}

http.ListenAndServe调用Server.ListenAndServe方法,该方法首先监听端口net.Listen("tcp", addr) ,然后调用了Server.Serve方法来等待请求的到来。

总结一下:
1.http.ListenAndServe创建Server类型变量并调用Server.ListenAndServe方法。
2.Server.ListenAndServe方法首先会监听TCP端口并调用Server.Serve方法来等待和处理请求。

三.处理请求

处理请求的逻辑也是在Server.Serve方法中。


func (srv *Server) Serve(l net.Listener) error {
    //....
    for {
        rw, e := l.Accept() //等待请求
        //...
        c := srv.newConn(rw)  //这里创建了一个conn结构体,它代表一个连接
        c.setState(c.rwc, StateNew) // before Serve can return
        go c.serve(ctx)
    }
}       
        

调用conn.serve方法:


func (c *conn) serve(ctx context.Context) {
        //...
        serverHandler{c.server}.ServeHTTP(w, w.req)
    //...
}

func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) {
    handler := sh.srv.Handler
    if handler == nil {
        handler = DefaultServeMux
    }
    handler.ServeHTTP(rw, req)
}

conn.serve调用serverHandler的ServeHTTP方法,这个方法会调用DefaultServeMux(如果我们没有手动设置新的ServerMux的话)的ServeHTTP方法。

到目前为止,我们已经知道实现ServeHTTP的类型有两个:ServerMux和HandlerFunc,也就是他们都实现了下面的Handler接口。


type Handler interface {
    ServeHTTP(ResponseWriter, *Request)
}

这样说来ServeMux和HandlerFunc本质上都是用来相应请求的handler。ServerMux的m字段保存了我们注册的多个HandlerFunc。HandlerFunc作为一个Handler接口的实现是我们自己编写的用来处理请求的,那么ServerMux做为Handler接口的实现的实现是用来干什么的呢?这里可以直接给出答案:是用来做路由的。
看一下ServerMux的ServeHTTP函数:


func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) {
    //...
    h, _ := mux.Handler(r)  //获取对应的handler
    h.ServeHTTP(w, r)    //执行handler
}


func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) {
    //...
    return mux.handler(host, r.URL.Path)   //不继续深究,有兴趣看源码,这里返回的是一个handler
}

上面代码很简单,mux.handler的实现是调用的mux.match函数对path进行匹配并返回handler的,这里就不深究了。

总结:

1.Handler接口:
包含ServeHTTP方法,实现这个接口得有ServeMuxHandlerFuncconn也实现了,这里不介绍)。

2.ServeMux结构体:
1.1 它的m字段用于存储我们注册的handler
1.2 它实现了Handler接口,它的ServeHTTP方法提供了路由功能。

3.HandlerFunc结构体:
它实现了Handler接口,我们编写的handler都会被转换为HandlerFunc类型,它的ServeHTTP方法会执行我们注册的handler

3.Server结构体:
监听指定端口,当请求到达的时候创建一个conn对象并执行它的ServeHTTP方法。

4.conn结构体:
一个conn代表一个连接,它的ServeHTTP方法会调用ServeMuxServeHTTP方法进行路由。

猜你喜欢

转载自www.cnblogs.com/kidyuan/p/9727096.html