Go对webserver的编写提供了很好的支持,标准库中提供了net/http包来方便编写server。很多教程和书籍在讲到用Go编写webserver时都会直接教新手用http包写一个最简单的hello worldserver,样例几乎相同都会像这样:

// 这就是用Go实现的一个最简短的hello worldserver.
package main import "net/http" func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(`hello world`))
})
http.ListenAndServe(":3000", nil) // <-今天讲的就是这个ListenAndServe是怎样工作的
}

能够看到。代码真的很简短,仅仅须要几行。我们今天要分析的是http.ListenAndServe(),看看这里面究竟都做了些什么。

首先,http.ListenAndServe用到的全部依赖都在Go源代码中的/src/pkg/net/http/server.go文件里,打开它会发现这页代码很长,有2000+行,我们Ctrl+F直接找我们感兴趣的部分。发如今1770行左右的部分找到了http.ListenAndServe的定义:

func ListenAndServe(addr string, handler Handler) error {
// 创建一个Server结构体,调用该结构体的ListenAndServer方法然后返回
server := &Server{Addr: addr, Handler: handler}
return server.ListenAndServe()
}

从这个函数中就能够看出,调用http.ListenAndServe之后真正起作用的是Server结构体LisntenAndServe方法。给http.ListenAndServe传递的參数仅仅是用来创建一个Server结构体实例,Server结构体的定义例如以下:

type Server struct {
Addr string // server的IP地址和port信息
Handler Handler // 请求处理函数的路由复用器
ReadTimeout time.Duration
WriteTimeout time.Duration
MaxHeaderBytes int
TLSConfig *tls.Config
TLSNextProto map[string]func(*Server, *tls.Conn, Handler)
ConnState func(net.Conn, ConnState)
ErrorLog *log.Logger
disableKeepAlives int32
}

假设我们不传详细的參数给http.ListenAndServe,那么它会自己主动以":http"(等价于":80")和DefaulServeMux作为參数来创建Server结构体实例。



接下来继续看看Server.ListenAndServe里面都做了些什么,1675行左右能够找到定义:

func (srv *Server) ListenAndServe() error {
addr := srv.Addr
if addr == "" {
addr = ":http" // 假设不指定server地址信息。默认以":http"作为地址信息
}
ln, err := net.Listen("tcp", addr) // 这里创建了一个TCP Listener,之后用于接收client的连接请求
if err != nil {
return err
}
return srv.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)}) // 调用Server.Serve()函数并返回
}

能够看到。Server.ListenAndServe中创建了一个serverListener。然后在返回时把它传给了Server.Serve()方法并调用Server.Serve()。

继续分析Server.Serve,定义的位置在1690行左右:

func (srv *Server) Serve(l net.Listener) error {
defer l.Close()
var tempDelay time.Duration
// 这个循环就是server的主循环了,通过传进来的listener接收来自client的请求并建立连接,
// 然后为每个连接创建routine运行c.serve()。这个c.serve就是详细的服务处理了
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
}
srv.logf("http: Accept error: %v; retrying in %v", e, tempDelay)
time.Sleep(tempDelay)
continue
}
return e
}
tempDelay = 0
c, err := srv.newConn(rw)
if err != nil {
continue
}
c.setState(c.rwc, StateNew) // before Serve can return
go c.serve() // <-这里为每个建立的连接创建routine之后进行服务
}
}

我们能够接着看看这个conn.serve()里面是怎么进行服务的。代码在1090行附近,仅仅看当中的主要部分:

func (c *conn) serve() {
origConn := c.rwc // copy it before it's set nil on Close or Hijack // 这里做了一些延迟释放和TLS相关的处理... // 前面的部分都能够忽略,这里才是基本的循环
for {
w, err := c.readRequest() // 读取client的请求
// ...
serverHandler{c.server}.ServeHTTP(w, w.req) //这里对请求进行处理
if c.hijacked() {
return
}
w.finishRequest()
if w.closeAfterReply {
if w.requestBodyLimitHit {
c.closeWriteAndWait()
}
break
}
c.setState(c.rwc, StateIdle)
}
}

经过一路的分析,http.ListenAndServe工作的流程就几乎相同明晰了,我们能够总结成一张流程图:



假设转载请注明出处:http://blog.csdn.net/gophers

最新文章

  1. iOS 读取大文件时候的注意点
  2. java问题排查可能用到的一些命令
  3. ArcEngine编写WebService
  4. 关于vue.js 组件的调用
  5. Delphi 程序结构
  6. Python3.5 入门学习记录——变量类型
  7. Eclipse插件基础篇一
  8. APUE学习--网络编程(3)
  9. MTK平台Android中常用的路径
  10. Nginx反向代理和负载均衡的配置
  11. 团队作业4——第一次项目冲刺(Alpha版本)2017.4.24
  12. Java后台模拟发送http的get和post请求,并测试
  13. 【转载】ASP.NET Core Web 支付功能接入 微信-扫码支付篇
  14. Oracle数据泵远程导入数据
  15. Python filter() 函数
  16. 《深入理解java虚拟机》读书笔记——垃圾收集与内存分配策略
  17. UoW中修改VIM的配色方案
  18. 【转】FFmpeg 基本用法
  19. css颜色表示法
  20. 「Java基本功」一文读懂Java内部类的用法和原理

热门文章

  1. iOS类方法实例方法 与 self
  2. [转]自定义Drawable实现灵动的红鲤鱼动画(下篇)
  3. Hotaru&amp;#39;s problem(hdu5371+Manacher)多校7
  4. 2019年所有人必须要掌握的一个技能 - “AI思维”
  5. jprofiler_windows-x64_9_1注册码
  6. js输出指定n位数的随机数的随机整数方法【转发】
  7. EXCEPTION-JS
  8. oracle默认连接数150
  9. 更改npm全局模块和cache默认安装位置
  10. iOS archive(归档)