一个简单的web服务
package main
import (
"io"
"log"
"net/http"
)
func HelloGoServer(w http.ResponseWriter, req *http.Request) {
io.WriteString(w, "Hello, this is a GoServer")
}
func main() {
http.HandleFunc("/", HelloGoServer)
err := http.ListenAndServe(":9090", nil)
if err != nil {
log.Fatal("ListenAndServer ", err)
}
}
运行上面的code,然后浏览器中输入:localhost:9090/。浏览器会输出:Hello, this is a GoServer。这里我们的浏览器是一个客户端,上面那段程序执行时是一个服务端,这两个角色不要搞反了。
下面我一一来分享我的分析过程,主要是从源码入手,同时参考网络牛人写的心得,他们这些文字我后面的参考有列出,强烈建议看看。
http包分析
HandleFunc分析
从main入手,先看第一句:
http.HandleFunc("/", HelloGoServer)
查看函数HandleFunc的源码,在server.go中,如果你用的是vim,参考重温vim的配置:支持go,配置好vim后,vim打开上面的代码,光标停留在HandleFunc,敲入gd,会很轻松的跳转过去看到相应的源码 。
在server.go中有两个HandleFunc接口函数,一个是有接收者,一个是没有接收者,很明显,main函数调用的是没有接收者的函数。
// HandleFunc registers the handler function for the given pattern
// in the DefaultServeMux.
// The documentation for ServeMux explains how patterns are matched.
func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
DefaultServeMux.HandleFunc(pattern, handler)
}
这里的DefaultServeMux是一个全局变量,查看声明的地方,
// DefaultServeMux is the default ServeMux used by Serve.
var DefaultServeMux = &defaultServeMux
var defaultServeMux ServeMux
你会发现有意思的是defaultServeMux还没有声明,就先把它的引用赋值给了DefaultServeMux。童鞋们,这与我们平时用的C、java不一样,go就是这么任性,why?具体参考Go语言基础:变量赋值顺序 。看完它就明白了。
再回到上面,接着调用的是有接收者的HandleFunc,如下:
// HandleFunc registers the handler function for the given pattern.
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
mux.Handle(pattern, HandlerFunc(handler))
}
Handle方法跟HandleFunc,也有两个,一个有接收者,一个没有接收者,这里HandleFunc调用的是有接收者的Handle方法,源码如下:
// Handle registers the handler for the given pattern.
// If a handler already exists for pattern, Handle panics.
func (mux *ServeMux) Handle(pattern string, handler Handler) {
...
}
从注释的英文翻译过来,该函数的功能就是为pattern注册一个handler。用这里的代码翻译就是HandleFunc的功能就是给”/”注册一个handler,这个handler就是HelloGoServer。
ListenAndServe
函数ListenAndServe字面上的解释是监听和服务。这里是监听9090端口。
server.go中同样有两个ListenAndServe,main函数调用的是不带接收者的函数ListenAndServe,然后再由它调用带接收者的函数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.
// Handler is typically nil, in which case the DefaultServeMux is
// used.
//
// A trivial example server is:
//
// package main
//
// import (
// "io"
// "net/http"
// "log"
// )
//
// // hello world, the web server
// func HelloServer(w http.ResponseWriter, req *http.Request) {
// io.WriteString(w, "hello, world!\n")
// }
//
// func main() {
// http.HandleFunc("/hello", HelloServer)
// log.Fatal(http.ListenAndServe(":12345", nil))
// }
//
// ListenAndServe always returns a non-nil error.
func ListenAndServe(addr string, handler Handler) error {
server := &Server{Addr: addr, Handler: handler}
return server.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.
// Handler is typically nil, in which case the DefaultServeMux is
// used.
翻译过来意思是ListenAndServe监听TCP网络的地址addr,然后调用服务处理程序handler来处理传入的连接请求。如果Handler是空的,则用默认的DefaultServeMux。
我们再来深入函数ListenAndServe,第一句定义Server类型server。Server类型定义http服务相关的参数,比如addr、handler等
// A Server defines parameters for running an HTTP server.
// The zero value for Server is a valid configuration.
type Server struct {
Addr string // TCP address to listen on, ":http" if empty
Handler Handler // handler to invoke, http.DefaultServeMux if nil
...
}
第二句return server.ListenAndServe()调用的是带接收者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.
func (srv *Server) ListenAndServe() error {
addr := srv.Addr
if addr == "" {
addr = ":http"
}
ln, err := net.Listen("tcp", addr)
if err != nil {
return err
}
return srv.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)})
}
函数Listen监听传入的addr地址,函数Serve等待浏览器输入对应的adder地址连接。
// 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.
//
// For HTTP/2 support, srv.TLSConfig should be initialized to the
// provided listener's TLS Config before calling Serve. If
// srv.TLSConfig is non-nil and doesn't include the string "h2" in
// Config.NextProtos, HTTP/2 support is not enabled.
//
// Serve always returns a non-nil error.
func (srv *Server) Serve(l net.Listener) error {
defer l.Close()
if fn := testHookServerServe; fn != nil {
fn(srv, l)
}
var tempDelay time.Duration // how long to sleep on accept failure
if err := srv.setupHTTP2_Serve(); err != nil {
return err
}
// TODO: allow changing base context? can't imagine concrete
// use cases yet.
baseCtx := context.Background()
ctx := context.WithValue(baseCtx, ServerContextKey, srv)
ctx = context.WithValue(ctx, LocalAddrContextKey, l.Addr())
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 := srv.newConn(rw)
c.setState(c.rwc, StateNew) // before Serve can return
go c.serve(ctx)
}
}
for循环中的accept()等待浏览器输入地址连接,一旦连接成功后,会新建一个Conn,并新开一个goroutine,启动conn服务。对应的源码如下:
// Serve a new connection.
func (c *conn) serve(ctx context.Context) {
...
serverHandler{c.server}.ServeHTTP(w, w.req)
...
}
服务中会调用ServeHTTP,即我们传入的handler函数。呃,这里明明是两个不通的函数,怎么会有关联呢?
ServeHTTP
先来看看函数ServeHTTP的源码如下:
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}
// serverHandler delegates to either the server's Handler or
// DefaultServeMux and also handles "OPTIONS *" requests.
type serverHandler struct {
srv *Server
}
func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) {
handler := sh.srv.Handler
if handler == nil {
handler = DefaultServeMux
}
if req.RequestURI == "*" && req.Method == "OPTIONS" {
handler = globalOptionsHandler{}
}
handler.ServeHTTP(rw, req)
}
结合前面的web服务程序,我们传入的handler是空,在这里,会将默认的handler:DefaultServeMux传给了handler。
ServeHTTP是一个接口,对象HandlerFunc调用了接口ServeHTTP,对象实现接口ServeHTTP的源码如下:
// The HandlerFunc type is an adapter to allow the use of
// ordinary functions as HTTP handlers. If f is a function
// with the appropriate signature, HandlerFunc(f) is a
// Handler that calls f.
type HandlerFunc func(ResponseWriter, *Request)
// ServeHTTP calls f(w, r).
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
f(w, r)
}
不难发现即使调用的就是对象f函数。然后回到头重新分析下main的两个函数,不难理解这里调用的其实是函数HelloGoServer。
好,到这里,两个重要接口都分析完了。接下来我们分析其中所接触到的结构体以及对应的接口方法。
几个相关的结构体
ServerMux结构
ServeMux大致作用是,他有一张map表,map里的key记录的是r.URL.String(),而value记录的是一个方法,这个方法和ServeHTTP是一样的,这个方法有一个别名,叫HandlerFunc
type ServeMux struct {
mu sync.RWMutex
m map[string]muxEntry // 存放具体路由的信息,一个string对应一个mux实体,本例中"/"对应HelloGoServer
hosts bool // whether any patterns contain hostnames
}
type muxEntry struct {
explicit bool
h Handler
pattern string
}
ServeMux定义的一些方法有:
func (mux *ServeMux) match(path string) (h Handler, pattern string) //根据path查找handler和pattern
func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) // 内部调用(mux *ServeMux)handler根据host、path查找handler和pattern
func (mux *ServeMux) handler(host, path string) (h Handler, pattern string) //内部调用(mux *ServeMux) match,根据host,path查找handler和pattern
func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) //并不是前面所分析的ServeHTTP接口,因为前面分析的接收者是handler。这里也是查找handler,当ServeMux中没有要找的路由时,会调用NotFoundHandler,输出"404 page not found"。具体看源码。
func (mux *ServeMux) Handle(pattern string, handler Handler)
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) //前面已有分析,给pattern注册一个handler
http包也提供了外部使用的几个方法。但实际上是调用ServeMux的内部方法
func Handle(pattern string, handler Handler) { DefaultServeMux.Handle(pattern, handler) }
func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
DefaultServeMux.HandleFunc(pattern, handler)
}
Server结构
type Server struct {
Addr string // TCP address to listen on, ":http" if empty 监听的地址和端口
Handler Handler // handler to invoke, http.DefaultServeMux if nil 所有请求调用的handler
ReadTimeout time.Duration // maximum duration before timing out read of the request
WriteTimeout time.Duration // maximum duration before timing out write of the response
TLSConfig *tls.Config // optional TLS config, used by ListenAndServeTLS
MaxHeaderBytes int
TLSNextProto map[string]func(*Server, *tls.Conn, Handler)
ConnState func(net.Conn, ConnState)
ErrorLog *log.Logger
disableKeepAlives int32 // accessed atomically.
nextProtoOnce sync.Once // guards setupHTTP2_* init
nextProtoErr error // result of http2.ConfigureServer if used
}
Server实现的接口如下:
func (srv *Server) ListenAndServe() error // 监听server,监听到有请求时,调用handler。
func (srv *Server) Serve(l net.Listener) error //对某个端口进行监听,里面就是调用for进行accept的处理了
func (srv *Server) ListenAndServeTLS(certFile, keyFile string) error //开启https server服务,内部调用Serve
...
http包也提供了外部使用的几个方法。但实际上是调用Server的内部方法
func ListenAndServe(addr string, handler Handler) error //开启Http服务
func ListenAndServeTLS(addr string, certFile string, keyFile string, handler Handler) error //开启HTTPs服务
参考:
Golang Http Server源码阅读
http.ServeMux解析
golang(GO语言)http详解简单基础(1)
go语言的http包
有疑问加站长微信联系(非本文作者)