• http.ListenAndServe工作原理、DefaultServeMux的结构


    这个函数是http里最重要的一个函数,或者说是服务端代码整合的最终灵魂。
    httpListenAndServe(port string, handler Handler)
    第一个参数是监听的端口、第二个参数是根页面的处理函数,可以为空。

    接下来是它做了些什么。
    首先看源码吧还是
    func ListenAndServe(addr string, handler Handler) error {
    server := &Server{Addr: addr, Handler: handler}
    return server.ListenAndServe()
    首先创建了一个server对象(但是我们拿不到它的引用),调用了server的ListenAndServe()方法
    这个server对象,代表了一个正在跑的HTTP协议的Server程序。
    addr代表它监听的端口,handler代表它的根页面使用的handler处理对象

    server.ListenAndServe()方法(注意啊,这个是server对象的方法)
    func (srv *Server) ListenAndServe() error {
    if srv.shuttingDown() {//这儿是指的这个server是不是被关了
    return ErrServerClosed
    }
    addr := srv.Addr//取出server的监听端口号
    if addr == "" {
    addr = ":http"
    }
    ln, err := net.Listen("tcp", addr)//使用端口号监听端口,监听端口返回了一个listen对象
    if err != nil {
    return err
    }
    return srv.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)})
    }
    type tcpKeepAliveListener struct {
    *net.TCPListener
    }
    目前已经监听了端口了,并且获得了监听端口之后返回的listen对象。并通过listen对象
    创建了一个tcpKeepAliveListener(tcp长时间监听的listener)
    然后调用serve的Serve方法
    func (srv *Server) Serve(l net.Listener) error {
    .............................//忽略了一堆判断的特殊的情况,我们就看下面就好
    for {
    rw, e := l.Accept()//通过tcplistener(tcp监听器)接受来自客户端的请求,并返回一个Conn
    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//跳到这儿了,如果e == nil 的话,没有发生错误的情况
    c := srv.newConn(rw)//通过listener返回的Conn创建一个http包的Conn
    c.setState(c.rwc, StateNew) // 设置状态码
    go c.serve(ctx)//通过http的Conn创建一个协程来单独处理这个Conn,ctx是server的信息。在上面定义的
    }
    }
    以下是newConn
    func (srv *Server) newConn(rwc net.Conn) *conn {
    c := &conn{//通过Server和rwc创建的连接
    server: srv,
    rwc: rwc,
    }
    if debugServerConnections {
    c.rwc = newLoggingConn("server", c.rwc)
    }
    return c
    }

    总:创建Server(代表应用程序),监听接口,获取listener、listener接受客户端发送来的消息并返回一个net包的conn,通过这个conn,serve创建一个http包的conn,并且这个conn内有serve的引用。
    然后开启这个conn的协程。在conn的协程里做所有的处理操作

    go c.serve(ctx)所创建的协程,里面做了很多操作,比如request、respose、responseWriter
    等对象的创建,信息的整合,等等等等,最重要的是,将本次请求的url对应到Serve的Hander的map中,并调用它。
    获取Conn内部的url去Serve的Hand的Map集合中去匹配。匹配到的就调用那个处理方法,

    我们创建协程是在当listener接收到请求之后才创建的协程,而listener之前的操作都在main协程中操作,但是我们如果不请求的话,程序就会卡再listener.Accept()处,所以我们的http.ListenAndServer应该放在程序的最后。所有的准备操作都应该再ListenerAddServe操作之前执行。

    接下来我们看一下路由(可以看作是一个map,每次请求都会到这个map找对应的Handler)的结构
    以下是我们程序创建的Serve的其中一个字段:handler的map

    http包下的DefaultServeMux//默认的Serve路由集合
    http包有DefaultServeMux对象,该对象有一个map[url]Entry,Entry内部放着handler,
    serve创建的处理请求的协程会去DefaultServeMux中的map里通过url进行匹配,找到对应的handler处理函数。

    var DefaultServeMux = &defaultServeMux
    //这是它的字段
    type ServeMux struct {
    mu sync.RWMutex
    m map[string]muxEntry//url及muxEntry
    es []muxEntry // slice of entries sorted from longest to shortest.
    hosts bool // whether any patterns contain hostnames
    }

    type muxEntry struct {//muxEntry的字段
    h Handler,这个Handler就是我们的一个处理函数对象,它被包在了一个muxEntry中,放入ServeMux的map 中
    pattern string
    }

  • 相关阅读:
    wget
    android layout 布局属性
    Android 官方推荐 : DialogFragment 创建对话框
    Android 屏幕旋转 处理 AsyncTask 和 ProgressDialog 的最佳方案
    Android Fragment 真正的完全解析
    Android-Universal-Image-Loader 图片异步加载类库的使用(超详细配置)
    Visual Studio VS2013模块对于SAFESEH 映像是不安全的 怎么办
    PS 图层后面有索引两字怎么办
    PS 如何使用液化工具给人物减肥
    PS 图层后面有索引两字怎么办
  • 原文地址:https://www.cnblogs.com/mcmx/p/11390938.html
Copyright © 2020-2023  润新知