net/http包也是net包中的,是在net tcp协议基础上实现的http协议。底层调用的方法也是基于net dial里面的那几个方法,当然不止于这些,下面大致从http服务器创建开始说起。
一、创建http服务
先简单看下代码:
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, request *http.Request) {
fmt.Println("Hello World.")
fmt.Fprintf(w, "Hello World.\n")
})
err := http.ListenAndServe("0.0.0.0:8899", nil)
if err != nil {
fmt.Println("http listen failed.")
}
}
简单几行代码就搭建了一个http服务,在浏览器通过http://127.0.0.1:8899 访问一下就可以看到结果。是不是很简单,只需要两个方法,一个ListenAndServe 方法,它的作用就是启动服务端监听端口,监听8899端口过来的请求,请求过来以后怎么处理,就要看HandleFunc方法,这个是对请求过来后访问的路径绑定处理函数。为了简单就只有一个路径,如果想添加多个访问uri,就在多加几个HandleFunc即可。
HandleFunc 里面传递了两个参数w http.ResponseWriter, request *http.Request
这两个是什么,我们看下它们的介绍
// A ResponseWriter interface is used by an HTTP handler to
// construct an HTTP response.
type ResponseWriter interface {
Header() Header
Write([]byte) (int, error)
WriteHeader(statusCode int)
}
type Request struct {
Method string
URL *url.URL
Proto string // "HTTP/1.0"
ProtoMajor int // 1
ProtoMinor int // 0
Header Header
Body io.ReadCloser
GetBody func() (io.ReadCloser, error)
ContentLength int64
TransferEncoding []string
Close bool
Host string
Form url.Values
PostForm url.Values
MultipartForm *multipart.Form
Trailer Header
RemoteAddr string
RequestURI string
TLS *tls.ConnectionState
Cancel <-chan struct{}
Response *Response
ctx context.Context
}
做了下简化处理,毕竟原文注释太多太长了,从官方给出的说明可以看到,一个是进行请求数据处理的,一个是相应数据处理的。ResponseWriter是个接口,只要实现了这个接口就可以,net/http/server.go 文件里面有对应的实现方法,可以自己看下,而request是个结构体变量。我们都知道变量需要初始化赋值才可用,但从示例里面似乎没看到在哪赋值,直接HandleFunc 拿来就可以用了,那么赋值这一步操作在哪呢,只有两个方法,一个是调用,那么初始化这一步只可能在ListenAndServe方法里了,我们进入ListenAndServe方法里面看看。
func ListenAndServe(addr string, handler Handler) error {
server := &Server{Addr: addr, Handler: handler}
return server.ListenAndServe()
}
这里面没有,只是初始化了服务器变量,我们继续进入 server.ListenAndServe()
return srv.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)})
里面也没有,为了简化只贴出有用的代码,我们继续往下看。
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)
这里面代码就比较长了,我们只看有用的几个
1、rw, e := l.Accept() //获取一个连接请求,
2、c := srv.newConn(rw) //连接初始化一下
3、 go c.serve(ctx) //启动goroutine 来处理请求
从上一篇net dial文件分析,我们知道获取一个连接以后,就可以通过连接来读取数据,又因为这是http服务,为了处理多个连接,所以go采用的是IO多路复用模式 ,用goroutine来处理请求,我们继续往下看。
for {
w, err := c.readRequest(ctx)
...
...
serverHandler{c.server}.ServeHTTP(w, w.req)
...
w.cancelCtx()
w.finishRequest()
...
}
原方法太长做了简化处理,具体代码可自行查看,我们只看几个主要的地方
1、 w, err := c.readRequest(ctx)
2、 serverHandler{c.server}.ServeHTTP(w, w.req)
第一个方法就是初始化request变量的方法,在这个方法里初始化了request变量和response变量
func (c *conn) readRequest(ctx context.Context) (w *response, err error)
req, err := readRequest(c.bufr, keepHostHeader)
func readRequest(b *bufio.Reader, deleteHostHeader bool) (req *Request, err error)
这就是整个初始化request过程,通过这个方法,最终得到了Request变量,里面包含http请求的参数,如head头,url,cookie等信息。传递到HandleFunc里面,就可以直接拿来使用了。
说到这里,还要再说个地方,我们知道调用HandleFunc 方法就可以把url请求绑定到处理方法上,那么绑定以后为什么访问url的时候自动就会调用绑定方法,就在于上面说的2:serverHandler{c.server}.ServeHTTP(w, w.req) 这里,
type HandlerFunc func(ResponseWriter, *Request)
// ServeHTTP calls f(w, r).
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
f(w, r)
}
可以看到只要找到了url对应的绑定方法,就会调用,从而就实现了整个http服务器流程。type Handler interface {
ServeHTTP(ResponseWriter, Request)
}
在handler接口只要实现了ServeHTTP就可以自定义属于自己的http服务,我们熟知的Gin框架就是通过重写ServeHTTP 实现自己封装的gin.Context结构来传递信息。
总结:go net/http包内部还实现了很多处理,包括锁,goroutine数量限制很多逻辑,代码结构也封装的很好,可以多看看学习学习。