这是我参与8月更文挑战的第3天,活动详情查看: 8月更文挑战
Web 应用程序是一种可以通过 Web 访问的应用程序,Web 程序的最大好处是用户很容易访问应用程序,用户只需要有浏览器即可,不需要再安装其他软件。Web 应用对于身处互联网时代的我们来说太普遍。无论哪一种语言,只要它能够开发出与人类交互的软件,它就必然会支持 Web 应用开发。
本系列文章将会介绍 Go Web 的应用与实践。欢迎关注。
上一篇文章介绍了具体构建 Go Web 服务器。通过前面文章实例的实现,我们看到编写一个 Web 服务器其实很简单,只要调用 Http 包的两个函数就可以了。本文开始将会深入实现原理,具体讲解 Go 是如何接收和处理请求。
Web 工作的几个概念
Go 的 net/http 标准库分为客户端和服务端两个部分,主要包括如下的结构和函数:
- 与服务端相关的有:Server、ServerMux、Handler/HandlerFunc、Header、Request 和 Cookie;
- 与客户端相关的有:Client、Response、Header、Request 和 Cookie。
其中,Header、Request 和 Cookie 是客户端和服务端共同涉及的组成部分。我们重点关注服务端的功能。
服务器端的几个概念:
- Request:用户请求,用来解析用户的请求信息,包括 post、get、cookie、url 等信息;
- Response:服务端需要反馈给客户端的信息;
- Conn:用户的每次请求链接;
- Handler:处理请求和生成返回信息的处理逻辑
http 包执行流程为,首先创建 Listen Socket,监听指定的端口,等待客户端请求到来;然后 Listen Socket 接受客户端的请求,得到 Client Socket,接下来通过 Client Socket 与客户端通信;服务端处理客户端的请求,首先从Client Socket 读取 HTTP 请求的协议头,如果是 POST 方法,还可能要读取客户端提交的数据,然后交给相应的 Handler 处理请求,Handler 处理完毕准备好客户端需要的数据,通过 Client Socket 写给客户端。
处理器处理请求
在了解了 Go http 包执行的流程,我们将会具体分析处理器处理请求的过程。
前面小节的代码里面我们可以看到,Go 通过一个函数 ListenAndServe
来处理这些事情的,这个底层其实这样处理的:初始化一个 server 对象,然后调用了 net.Listen("tcp", addr)
,也就是底层用 TCP 协议搭建了一个服务,然后监控我们设置的端口。
下面代码来自 Go 的 http 包的源码,通过下面的代码我们可以看到整个的 http 处理过程:
func (srv *Server) Serve(l net.Listener) error {
l = &onceCloseListener{Listener: l}
defer l.Close()
//...
defer srv.trackListener(&l, false)
var tempDelay time.Duration // how long to sleep on accept failure
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)
}
}
复制代码
监听之后如何接收客户端的请求呢?上面代码执行监控端口之后,调用了 srv.Serve(net.Listener)
函数,这个函数就是处理接收客户端的请求信息。这个函数里面起了一个 for{}
循环,首先通过 Listener 接收请求,其次创建一个 Conn,最后单独开了一个 goroutine,把这个请求的数据当做参数给conn去服务:go c.serve()
。这就是高并发体现了,用户的每一次请求都是在一个新的 goroutine 去服务,相互不影响。
小结
本文主要介绍 Web 工作的几个概念以及处理请求的部分原理。下面的文章将会继续分析收发请求的其他过程。
阅读最新文章,关注公众号:aoho求索