内容简介:Golang Http Server 实现的学习Http 服务是基于 Tcp 的应用层的实现,也是我们常见的网络协议之一。go 语言提供了较为丰富的http协议的实现包golang 中, 连接的管理采用的是 Reactor 模式。每个请求到达服务器之后,都会分配一个 goroutine 做任务处理。
Golang Http Server 实现的学习
Http 服务是基于 Tcp 的应用层的实现,也是我们常见的网络协议之一。go 语言提供了较为丰富的http协议的实现包 net/http
包。http 是典型的C/S 架构(也是B/S架构),我们先从Server端入手,看看Http Server 是如何实现的。
请求连接的管理
golang 中, 连接的管理采用的是 Reactor 模式。每个请求到达服务器之后,都会分配一个 goroutine 做任务处理。
func (srv *Server) Serve(l net.Listener) error { // ... 初始化和验证listener // ... 构造 context for { rw, e := l.Accept() if e != nil { select { case <-srv.getDoneChan(): return ErrServerClosed default: } // ... 若为临时错误,启动重试机制 // 否则退出 } tempDelay = 0 c := srv.newConn(rw) c.setState(c.rwc, StateNew) // 创建goroutine, 单独处理连接 go c.serve(ctx) } }
我们在处理 http 请求时,不同请求在不同goroutine中,需要注意并发请求数据共享的问题。
连接的状态
Server 在Accept 后创建连接(conn),连接可能有多种状态。通过连接的状态转移,可以方便我们了解一个conn 的处理流程。下面是状态的转移图:
当Accept后,构建了新的连接,状态将标记为New。如果可以读取数据,连接将标记为Active(即,活动的Conn)。作为一个活动的Conn,可能在处理完毕后变为Idle状态用于请求复用;也有可能因为请求协议故障,变为Close状态;也有可能被服务调用方直接管理Conn,状态变更为Hijacked 状态。
Hijacked 状态下,Conn 被使用方自行管理,一般用于协议升级的情况。例如:通过http 请求后,协议升级为websocket 请求,或者Rpc 请求等。
连接的处理
做http 的连接处理,重点有几个方面:① 通过连接读取数据,并做协议分析和处理;②对http请求做处理(我们正常需要做的业务处理);③ 连接的复用和升级。
首先,我们看看整体的处理流程:
// Serve a new connection. func (c *conn) serve(ctx context.Context) { // ... defer 处理异常退出 和连接关闭 // ... tls 握手 // 初始化conn 的读写 // 对于keeplive 循环处理请求 for { // 读取/处理请求头 w, err := c.readRequest(ctx) // ... 连接状态变更 && 异常处理 // 对 Except 100-continue 的特殊处理。 // 原子包裹 response 对象 c.curReq.Store(w) // 异步读取Body (此处也有对 except 100 的处理) // 传入 request 和 response 处理 handler serverHandler{c.server}.ServeHTTP(w, w.req) // 连接复用判断, 复用则退出 // 请求结束,写header 和resp,并关闭req w.finishRequest() if !w.shouldReuseConnection() { if w.requestBodyLimitHit || w.closedRequestBodyEarly() { c.closeWriteAndWait() } return } // 更改状态,释放 response // 如果不需要keeplive, 连接将被关闭 if !w.conn.server.doKeepAlives() { return } // 判断是否超时,连接是否可用, 若不可用则关闭 // 重新设置超时 c.rwc.SetReadDeadline(time.Time{}) } }
从代码中可以看出,除了需要做Http 的解析外,还需要不断判断Conn 的状态。当进入Hijack状态后,不再控制Conn;当连接异常后,不再处理请求;当keeplive后,需要复用连接;超时之后,对连接的关闭等。此外,还需要对http 协议做适配处理,例如 对 Except: 100-continue的支持等。
对于每个请求,我们都会有一个 Request 和 Response 对象,分别标识一个请求和响应。从Request 中读取请求Body,将我们的响应写入Response对象中。下面我们来看看Server端是如何构造这两个对象的。
Request 的构造
- 首先是对协议头的解析,获取请求的方法、请求Url,协议等,如果是代理模式,还会做Url的替换。
- 然后会解析Header,在Server 中,Golang 的Header 数据是存储在 map[string][]string 结构中,Key 采用大驼峰和连字符描述。
- 对于Pragma:no-cache 的请求,标识 Cache-control:No-cache
- 对于Connection: close 的请求,不再keeplive
- 构造 Request 传输控制的数据:
- Transfer-Encoding 的修正
- Content-Length 的修正
- chunk 模式下的Trailer修正
- Body 的构造
- PRI header 对Http2的支持。(需要通过HiJack 支持)
Response 的构造
Response 作为服务的响应节点,比较简单,初始化:
w = &response{ conn: c, cancelCtx: cancelCtx, req: req, reqBody: req.Body, handlerHeader: make(Header), contentLength: -1, closeNotifyCh: make(chan bool, 1), // We populate these ahead of time so we're not // reading from req.Header after their Handler starts // and maybe mutates it (Issue 14940) wants10KeepAlive: req.wantsHttp10KeepAlive(), wantsClose: req.wantsClose(), } w.cw.res = w //创建 一个写的缓冲区 (这里还用到了 sync.Pool 做对象存储 w.w = newBufioWriterSize(&w.cw, bufferBeforeChunkingSize)
Handler 的学习
总结
- 一个 Http 请求,至少会启动两个goroutine。一个groutine用来处理请求,另一个goroutine 用来异步读取body 数据。
- http 的method 的合法性校验是
几个比较特殊的 Http 协议规则
- Http Except: 100-continue 协议
- Http CONNECT METHOD, 不仅会用在代理模式的Http Server中,还有可能用在RPC中。
- Chunk 模式, Trailer 设置
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 混合学习环境下基于学习行为数据的学习预警系统设计与实现
- 算法学习(JavaScript实现)
- 机器学习算法 Python 实现
- 如何基于深度学习实现图像
- Python实现机器学习算法
- [译] Deep CARs:使用 Pytorch 学习框架实现迁移学习
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
无懈可击的Web设计
【美】Dan Cederholm / 马跃 / 清华大学出版社 / 2012-5 / 39.00元
本书将指导您采用标准设计策略来满足以各种方式浏览网页的各类用户的需要。每章首先列举一个沿用传统HTML技术的实例,然后指出该实例的局限性,并利用XHTML和CSS对其进行重构。从中您将学会如何用简洁高效的HTML标记和CSS来取代臃肿的代码,从而创建加载速度极快、能供所有用户使用的网站。本书最后将前面各章讨论的所有页面组件珠联璧合地结合在一起,制作了一个页面模板。这一版全面润色和更新了上一版本,介......一起来看看 《无懈可击的Web设计》 这本书的介绍吧!