go语言可以用几行代码搭建一个http服务器,例如:
package main
import (
"fmt"
"net/http"
)
func SayHello(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello World!")
}
func main() {
http.HandleFunc("/", SayHello)
http.ListenAndServe(":8080", nil)
}
也可以自定义多路复用器和http.Server搭建一个http服务器:
package main
import (
"fmt"
"net/http"
)
func SayHello(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello World!")
}
func main() {
// 创建一个多路复用器
mux := http.NewServeMux()
// 绑定pattern和Handler函数
mux.HandleFunc("/", SayHello)
// 创建server 绑定多路复用器
server := http.Server{
Addr: ":8080",
Handler: mux,
TLSConfig: nil,
ReadTimeout: 0,
ReadHeaderTimeout: 0,
WriteTimeout: 0,
IdleTimeout: 0,
MaxHeaderBytes: 0,
TLSNextProto: nil,
ConnState: nil,
ErrorLog: nil,
BaseContext: nil,
ConnContext: nil,
}
// 开始监听
server.ListenAndServe()
}
搭建服务器的准备:
从上面的代码可以看到go语言的http服务器有三个必需对象:handler函数(SayHello)、多路复用器(mux)和httpServer(server)。
先来看看从handler函数到服务器启动的过程,首先创建一个和http.HandlerFunc签名相同的handler函数SayHello,也可以是实现http.Handler接口的类型的对象。然后创建一个多路复用器,mux := http.NewServeMux()。
再调用mux.HandleFunc("/", SayHello)函数绑定路由和handler函数的关系。mux.HandleFunc("/", SayHello)后面做了2件事。1,把函数SayHello转成HandlerFunc类型;2,调用mux.Handle(pattern string, handler Handler)方法把"/"和SayHello存储到mux内部的map对象和切片对象中。
mux.HandleFunc("/", SayHello)方法的源码
// HandleFunc registers the handler function for the given pattern.
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
if handler == nil {
panic("http: nil handler")
}
mux.Handle(pattern, HandlerFunc(handler))
}
mux.Handle(pattern, HandlerFunc(handler))的源码
// 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) {
mux.mu.Lock()
defer mux.mu.Unlock()
if pattern == "" {
panic("http: invalid pattern")
}
if handler == nil {
panic("http: nil handler")
}
if _, exist := mux.m[pattern]; exist {
panic("http: multiple registrations for " + pattern)
}
if mux.m == nil {
mux.m = make(map[string]muxEntry)
}
e := muxEntry{h: handler, pattern: pattern}
mux.m[pattern] = e
if pattern[len(pattern)-1] == '/' {
mux.es = appendSorted(mux.es, e)
}
if pattern[0] != '/' {
mux.hosts = true
}
}
然后创建相当于套接字功能的server对象,初始化server的Handler属性为mux,最后调用server.ListenAndServe()就完成了http服务器的最简单的搭建。
服务器处理请求的过程:
server.ListenAndServe()方法创建了一个套接字对象,并调用server.Serve(l net.Listener) 方法监听该套接字。
// ListenAndServe always returns a non-nil error. After Shutdown or Close,
// the returned error is ErrServerClosed.
func (srv *Server) ListenAndServe() error {
if srv.shuttingDown() {
return ErrServerClosed
}
addr := srv.Addr
if addr == "" {
addr = ":http"
}
ln, err := net.Listen("tcp", addr)
if err != nil {
return err
}
return srv.Serve(ln)
}
在server.Serve(l net.Listener)方法内部有个死循环一直监听套接字,一旦有数据被接收就会创建一个连接c,并调用创建一个 serve(ctx context.Context)函数协程。
// Serve always returns a non-nil error and closes l.
// After Shutdown or Close, the returned error is ErrServerClosed.
func (srv *Server) Serve(l net.Listener) error {
// 代码太长 省略
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
}
connCtx := ctx
if cc := srv.ConnContext; cc != nil {
connCtx = cc(connCtx, rw)
if connCtx == nil {
panic("ConnContext returned nil")
}
}
tempDelay = 0
c := srv.newConn(rw)
c.setState(c.rwc, StateNew) // before Serve can return
go c.serve(connCtx)
}
}
在c.serve(ctx context.Context)方法中调用了serverHandler{c.server}.ServeHTTP(w, w.req),这里把c.server即上面文章前面搭建服务器的准备中介绍的相当于套接字功能的server对象,serverHandler类型实现了Handler接口。
// Serve a new connection.
func (c *conn) serve(ctx context.Context) {
// 源码太长 省略掉其他部分
serverHandler{c.server}.ServeHTTP(w, w.req)
// 源码太长 省略掉其他部分
}
看一下serverHandler.ServeHTTP(w,w.req)方法源码,handler := sh.srv.Handler,handler其实是server.Handler,还记得在创建server对象时初始化的Handler属性值吗?就是mux(多路复用器),这个方法最终调用的是mux.ServeHTTP(rw, req)。
// 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)
}
在mux.ServeHTTP(rw, req)内部调用了mux.Handler(r)方法得到handler函数对象,并执行这个函数。
// ServeHTTP dispatches the request to the handler whose
// pattern most closely matches the request URL.
func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) {
if r.RequestURI == "*" {
if r.ProtoAtLeast(1, 1) {
w.Header().Set("Connection", "close")
}
w.WriteHeader(StatusBadRequest)
return
}
h, _ := mux.Handler(r)
h.ServeHTTP(w, r)
}
至此一个请求的基本过程就完成了,但还有两点要解释。1,mux.Handler(r)根据request请求返回handler函数时调用了mux.handler(host, r.URL.Path),在mux.handler(host, r.URL.Path)内部又调用了mux.match(path)方法,最终这个match(path string) (h Handler, pattern string)方法才是根据请求url查询handler函数的重点。
// If there is no registered handler that applies to the request,
// Handler returns a ``page not found'' handler and an empty pattern.
func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) {
// 代码太长 省略
return mux.handler(host, r.URL.Path)
}
// handler is the main implementation of Handler.
// The path is known to be in canonical form, except for CONNECT methods.
func (mux *ServeMux) handler(host, path string) (h Handler, pattern string) {
mux.mu.RLock()
defer mux.mu.RUnlock()
// Host-specific pattern takes precedence over generic ones
if mux.hosts {
h, pattern = mux.match(host + path)
}
if h == nil {
h, pattern = mux.match(path)
}
if h == nil {
h, pattern = NotFoundHandler(), ""
}
return
}
match(path string) (h Handler, pattern string)方法是从多路复用器mux内部map对象和切片对象寻找相同的请求路径或者长得像的请求路径,并返回handler函数,这里的请求路径和handler函数就是上文调用mux.HandleFunc("/", SayHello)绑定的请求路径和handler函数。
func (mux *ServeMux) match(path string) (h Handler, pattern string) {
// Check for exact match first.
v, ok := mux.m[path]
if ok {
return v.h, v.pattern
}
// Check for longest valid match. mux.es contains all patterns
// that end in / sorted from longest to shortest.
for _, e := range mux.es {
if strings.HasPrefix(path, e.pattern) {
return e.h, e.pattern
}
}
return nil, ""
}
2,由于上文中我使用的是mux.HandleFunc("/", SayHello)绑定请求路径和handler函数,所以调用mux.Handler(r)返回的其实是一个http.HandlerFunc类型的函数,并且http.HandlerFunc也实现了Handler接口,可以看到http.HandlerFunc的ServeHTTP(w ResponseWriter, r *Request)方法实现的是调用自身,即绑定的handler函数。
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
f(w, r)
}
参考一下思维导图
附本文参考书籍:
参考文献:https://github.com/ffhelicopter/Go42/blob/master/content/42_36_http.md