golang之web编程执行流程

为什么golang做web编程比其他语言并发高:


Go是通过一个函数ListenAndServe来处理这些事情的,这个底层其实这样处 理的:初始化一个server对象,然后调用了net.Listen("tcp",addr),也就是底层用TCP协议搭建了一个服 务,然后监控我们设置的端口。
下面代码来自Go的http包的源码,通过下面的代码我们可以看到整个的http处理过程:

func (srv *Server) Serve(l net.Listener) error {
    defer l.Close()
    var tempDelay time.Duration // how long to sleep on accept failure
    for {
        rw,e := l.Accept() 
        if e != nil {
            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
                }
                log.Printf("http: Accept error: %v; retrying in %v",e,tempDelay) 
                time.Sleep(tempDelay)
                continue
            }
            return e 
        }

        tempDelay = 0
        if srv.ReadTimeout != 0 {
            rw.SetReadDeadline(time.Now().Add(srv.ReadTimeout)) 
        }
        if srv.WriteTimeout != 0 {
            rw.SetWriteDeadline(time.Now().Add(srv.WriteTimeout))
        }
        c,err := srv.newConn(rw) if err != nil {
            continue 
        }
        go c.serve()
    }
    panic("not reached")
}

监控之后如何接收客户端的请求呢?上面代码执行监控端口之后,调用了srv.Serve(net.Listener)函数,这个 函数就是处理接收客户端的请求信息。这个函数里面起了一个for{},首先通过Listener接收请求,其次创建一个 Conn,最后单独开了一个goroutine,把这个请求的数据当做参数扔给这个conn去服务:go c.serve()。这个就是 高并发体现了,用户的每一次请求都是在一个新的goroutine去服务,相互不影响。
那么如何具体分配到相应的函数来处理请求呢?conn首先会解析request:c.readRequest(),然后获取相应的 handler:handler := c.server.Handler,也就是我们刚才在调用函数ListenAndServe时候的第二个参数,我们前面例子传递的是nil,也就是为空,那么默认获取handler = DefaultServeMux,那么这个变量用来做什么 的呢?对,这个变量就是一个路由器,它用来匹配url跳转到其相应的handle函数,那么这个我们有设置过吗?有,我 们调用代码里面第一句不是调用了http.HandleFunc("/",sayhelloName)嘛。这个作用就是注册了请求/的 路由规则,当请求uri为"/",路由就会转到函数sayhelloName,DefaultServeMux会调用ServeHTTP方法,这个方法内 部其实就是调用sayhelloName本身,最后通过写入response的信息反馈到客户端。

Go代码的执行流程 通过对http包的分析之后,现在让我们来梳理一下整个的代码执行过程。
首先调用Http.HandleFunc
按顺序做了几件事:
1 调用了DefaultServerMux的HandleFunc
2 调用了DefaultServerMux的Handle
3 往DefaultServeMux的map[string]muxEntry中增加对应的handler和路由规则 其次调用http.ListenAndServe(":9090",nil)
按顺序做了几件事情:
1 实例化Server
2 调用Server的ListenAndServe()
3 调用net.Listen("tcp",addr)监听端口
4 启动一个for循环,在循环体中Accept请求
5 对每个请求实例化一个Conn,并且开启一个goroutine为这个请求进行服务go c.serve()
6 读取每个请求的内容w,err := c.readRequest()
7 判断handler是否为空,如果没有设置handler(这个例子就没有设置handler),handler就设置为 DefaultServeMux
8 调用handler的ServeHttp
9 在这个例子中,下面就进入到DefaultServerMux.ServeHttp
10 根据request选择handler,并且进入到这个handler的ServeHTTP mux.handler(r).ServeHTTP(w,r)
11 选择handler:
A 判断是否有路由能满足这个request(循环遍历ServerMux的muxEntry)
B 如果有路由满足,调用这个路由handler的ServeHttp
C 如果没有路由满足,调用NotFoundHandler的ServeHttp

相关文章

程序目录结构 简单实现,用户登录后返回一个jwt的token,下次请求带上token请求用户信息接口并返回信息...
本篇博客的主要内容是用go写一个简单的Proof-of-Work共识机制,不涉及到网络通信环节,只是一个本地的简...
简介 默克尔树(MerkleTree)是一种典型的二叉树结构,其主要特点为: 最下面的叶节点包含存储数据或其...
接下来学习并发编程, 并发编程是go语言最有特色的地方, go对并发编程是原生支持. goroutine是go中最近本...
先普及一下, 什么是广度优先搜索 广度优先搜索类似于树的层次遍历。从图中的某一顶点出发,遍历每一个顶...
第一天: 接口的定义和实现 第二天: 一. go语言是面向接口编程. 在学习继承的时候说过, go语言只有封装,...