Go http包执行流程

Go 语言实现的 Web 服务工作方式与其他形式下的 Web 工作方式并没有什么不同,具体流程如下:

—— http包执行流程

Request:来自用户的请求信息,包括 post、get、Cookie、url 等。

Response:服务器返回给客户端的信息。

Connect:用户的每次的请求连接

Handler:处理请求和生成返回信息的处理逻辑

根据上图,Go 语言中的 http 包具体做了这么三个操作:

  1. 创建 Listen Socket,监听指定端口,等待客户端请求。
  2. Listen Socket 接受客户端请求,得到 Client Socket,接下来通过 Client Socket与客户端通信
  3. 处理客户端请求。首先从 Client Socket 获取 HTTP 请求数据,然后交给相应的 handler 处理请求,handler 处理完毕后再通过 Client Socket 返回给客户端。

接着我们从代码的角度来看一下这三个操作是如何实现的:

一,监听端口

在 Go 语言中只需要通过调用 ListenAndServe 方法即可设置监听端口:

func main() {
    http.HandleFunc("/", sayhelloName)       //设置访问的路由

    err := http.ListenAndServe(":9090", nil) //设置监听的端口
    if err != nil {
        log.Fatal("ListenAndServe: ", err)
    }
}

我们接着看一下 ListenAndServe 方法的具体实现,看一下它是如何实现监听端口的:

// ListenAndServe listens on the TCP network address addr and then calls
// Serve with handler to handle requests on incoming connections.
// Accepted connections are configured to enable TCP keep-alives.
//
// The handler is typically nil, in which case the DefaultServeMux is used.
//
// ListenAndServe always returns a non-nil error.
func ListenAndServe(addr string, handler Handler) error {
    server := &Server{Addr: addr, Handler: handler}
    return server.ListenAndServe()
}

通过查看该方法的源码,可以发现该方法初始化了一个 server 对象,并调用了该 server 对象的 ListenAndServe() 方法:

// ListenAndServe listens on the TCP network address srv.Addr and then
// calls Serve to handle requests on incoming connections.
// Accepted connections are configured to enable TCP keep-alives.
//
// If srv.Addr is blank, ":http" is used.
//
// ListenAndServe always returns a non-nil error. After Shutdown or Close,
// the returned error is ErrServerClosed.
func (srv *Server) ListenAndServe() error {
    if srv.shuttingDown() {
        return ErrServerClosed
    }
    addr := srv.Addr
    if addr == "" {
        addr = ":http"
    }
    ln, err := net.Listen("tcp", addr) //底层使用tcp协议搭建了一个服务,然后监听所设置的端口
    if err != nil {
        return err
    }
    return srv.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)})
}

在这个方法中调用了 net.Listen("tcp", addr) 来监听端口

二,接收客户端请求

在完成了对端口的监听之后,再通过调用 srv.Serve(net.Listener) 方法来处理接收客户端的请求消息。

// Serve accepts incoming connections on the Listener l, creating a
// new service goroutine for each. The service goroutines read requests and
// then call srv.Handler to reply to them.
//
// HTTP/2 support is only enabled if the Listener returns *tls.Conn
// connections and they were configured with "h2" in the TLS
// Config.NextProtos.
//
// Serve always returns a non-nil error and closes l.
// After Shutdown or Close, the returned error is ErrServerClosed.
func (srv *Server) Serve(l net.Listener) error {
    ...

    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
        }

        ...
        c := srv.newConn(rw)
        c.setState(c.rwc, StateNew) // before Serve can return
        go c.serve(ctx)
    }
}

在这个方法中,启动了一个 for 循环使 Listener 不断地接收来自客户端的请求,并且对每一个请求都实例化一个 Conn,并开启一个 goroutine 来为这个请求进行服务 go c.serve()。用户的每一次请求都是在一个新的 goroutine中服务,互相不影响。

三,为不同的请求分配处理逻辑

对于来自客户端的不同请求,服务器端需要根据情况来分配相对应的函数进行处理。

在之前的 main 方法中,我们调用了 http.ListenAndServe(":9090", nil) 来监听端口,而第二个参数传入的是 nil,那么这个参数是干嘛用的呢?

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

type Server struct {
    Addr    string      // 监听的地址和端口 默认为":http"
    Handler Handler     // 路由管理,如果为nil,则默认为http.DefaultServeMux
    ReadTimeout time.Duration //读的最大超时时间
    WriteTimeout time.Duration //写的最大超时时间
    MaxHeaderBytes int         //请求头的最大长度
    TLSConfig *tls.Config      // 配置TLS
    ...
}

从源码中的注释可以看出,当初始化 Server 时,如果不指定 Handler,则默认获取 Handler = http.DefaultServeMux。这个 Handler 就是一个路由器,它用来匹配 url 跳转到其相应的 handle 函数。

// DefaultServeMux is the default ServeMux used by Serve.
var DefaultServeMux = &defaultServeMux
var defaultServeMux ServeMux

详细流程如下:

—— http处理连接流程

原文地址:https://www.cnblogs.com/liyutian/p/10144584.html

时间: 2024-11-16 17:50:28

Go http包执行流程的相关文章

(一)熟悉执行流程——基于ThinkPHP3.2的内容管理框架OneThink学习

ThinkPHP作为国内具有代表性的PHP框架,经过多年的发展,受到越来越多公司与开发者的青睐.我也在忙里偷闲中抽出部分时间,来学习这个优秀的框架.在开始学习这个框架时,最好通过实例来学习,更容易结合实际的生产情况,促进学习的效果:这里我就选择由ThinkPHP团队开发的基于ThinkPHP3.2的内容管理框架OneThink来学习,从了解它的执行流程→熟悉流程中各个细节→了解模版标签→自己实际去使用标签→再了解它的实际执行过程……通过这样一个流程来熟悉如何基于ThinkPHP开发出一套CMS系

linux内核数据包转发流程(二)中断

[版权声明:转载请保留出处:blog.csdn.net/gentleliu.邮箱:shallnew*163.com] 内核在处理2层数据包之前,必须先处理中断系统,设立中断系统,才有可能每秒处理成千的帧. 当收到一个帧时,驱动程序会代表内核指示设备产生一个硬件中断,内核将中断其他的活动,然后调用一个驱动程序所注册的处理函数,以满足设备的需要.当事件是接收到一个帧时,处理函数就会把该帧排入队列某处,然后通知内核. 使用轮询技术会轻易浪费掉很多系统资源,因为内核会持续去读取检查是否有有帧的到来.但使

Struts2框架的执行流程

一.执行流程介绍 1.通过浏览器发送一个请求 2.会被StrutsPrepareAndExecuteFilter拦截(struts2的拦截器作用) 3.会调用strust2框架默认的拦截器(interceptor)完成部分功能 4.在执行Action中操作 5.根据Action中方法的执行结果来选择来跳转页面Result视图 一般我们将StrutsPrepareAndExecuteFilter 叫做前端控制器(核心控制器),只有在web.xml文件中配置了这个filter我们的strtus2框架

Android中图片加载框架Glide解析2----从源码的角度理解Glide的执行流程

转载地址:http://blog.csdn.net/guolin_blog/article/details/53939176 在本系列的上一篇文章中,我们学习了Glide的基本用法,体验了这个图片加载框架的强大功能,以及它非常简便的API.还没有看过上一篇文章的朋友,建议先去阅读 Android图片加载框架最全解析(一),Glide的基本用法 . 在多数情况下,我们想要在界面上加载并展示一张图片只需要一行代码就能实现,如下所示: Glide.with(this).load(url).into(i

nodejs require执行流程

为什么要加这个必读!因为webpack本身是基于node环境的, 里面会涉及很多路径问题,我们可能对paths怎么写!webpack又是怎么找到这些paths的很迷惑. 本文是我已经写完正式学习React(五)后新加的!我觉得很有必要让大家知道! ------------------------------------------------------------------------------------------------------ nodejs require执行流程  ===

linux 内核网络数据包接收流程

转:https://segmentfault.com/a/1190000008836467 本文将介绍在Linux系统中,数据包是如何一步一步从网卡传到进程手中的. 如果英文没有问题,强烈建议阅读后面参考里的两篇文章,里面介绍的更详细. 本文只讨论以太网的物理网卡,不涉及虚拟设备,并且以一个UDP包的接收过程作为示例. 本示例里列出的函数调用关系来自于kernel 3.13.0,如果你的内核不是这个版本,函数名称和相关路径可能不一样,但背后的原理应该是一样的(或者有细微差别) 网卡到内存 网卡需

struts2的执行流程与配置详解

本章主要讲解Struts的执行流程以及Struts的配置以及访问servletApi 全部代码下载: github链接:链接 写文章不易,欢迎大家采我的文章,以及给出有用的评论,当然大家也可以关注一下我的github:多谢: 1.Struts的执行流程: 1.服务器启动时: 加载项目web.xml 创建Struts核心过滤器对象, 执行StrutsPrepareAndExecuteFilter的doFilter 的 init()方法: 在StrutsPrepareAndExecuteFilter

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

linux内核数据包转发流程(三)网卡帧接收分析

[版权声明:转载请保留出处:blog.csdn.net/gentleliu.邮箱:shallnew*163.com] 每个cpu都有队列来处理接收到的帧,都有其数据结构来处理入口和出口流量,因此,不同cpu之间没有必要使用上锁机制,.此队列数据结构为softnet_data(定义在include/linux/netdevice.h中): /* * Incoming packets are placed on per-cpu queues so that * no locking is neede