简单使用
package main
import "net/http"
func main(){
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("hello world"))
})
http.ListenAndServe("", nil)
}
通过 net/http 包调用 HandleFunc 进行路由注册,其中第一个参数 pattern 是请求路径, 第二个参数handler 是请求处理参数,
调用ListenAndServe 函数,并传入 网络地址 和 请求处理器,当 网络地址 参数为空字符串,那么默认使用 80 端口进行连接;处理器参数为 nil 时服务端会使用默认的多路复用器 DefaultServeMux
源码分析
路由分配
http.HandleFunc()
func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
DefaultServeMux.HandleFunc(pattern, handler)
}
DefaultServeMux 是默认的请求处理器 ServeMux,ServeMux 使用map保存了注册的所有路径和处理函数的对应关系,结构如下
type ServeMux struct {
mu sync.RWMutex // 锁
m map[string]muxEntry // 路由规则,一个string对应一个mux实体,这里的string就是注册的路由表达式
es []muxEntry // slice of entries sorted from longest to shortest.
hosts bool // 是否有任何路径包含主机名
}
其中 muxEntry 定义如下
type muxEntry struct {
h Handler // 路由对应哪个handler,handler是一个接口,包含ServeHTTP(ResponseWriter, *Request)方法
pattern string // 匹配字符串
}
再看一下 ServeMux.HandleFunc()这个方法
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
if handler == nil {
panic("http: nil handler")
}
// 将func表示的handler转换为net/http中的HandlerFunc,并在其接口ServerHTTP中调用handler.
mux.Handle(pattern, HandlerFunc(handler))
}
HandlerFunc 实际上是以函数类型 func(ResponseWriter, *Request) 为底层类型,为 HandlerFunc 类型定义了方法 ServeHTTP()
type HandlerFunc func(ResponseWriter, *Request)
Go语言中允许为(基于)函数的类型定义方法,Serve.Handle() 方法只接受类型为接口Handler的参数:
// ServeHTTP calls f(w, r)
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
f(w, r)
}
HandlerFunc(handler) 将我们自己的处理函数 handler 转换成了 HandlerFunc 类型,使结构体实现了net\http 下的 Handler 接口,并重写了ServerHTTP方法,在该方法内部直接调用我们自己的handler
最后调用 mux.Handle() 方法进行注册
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
}
}
端口监听
首先看一下 http.ListenAndServe("", nil) 这个方法
func ListenAndServe(addr string, handler Handler) error {
server := &Server{Addr: addr, Handler: handler}
return server.ListenAndServe()
}
生成 Server 结构体,调用其 ListenAndServe 方法
其中 Server 结构体的定义如下
type Server struct {
Addr string
Handler Handler
DisableGeneralOptionsHandler bool
TLSConfig *tls.Config
ReadTimeout time.Duration
ReadHeaderTimeout time.Duration
WriteTimeout time.Duration
IdleTimeout time.Duration
MaxHeaderBytes int
TLSNextProto map[string]func(*Server, *tls.Conn, Handler)
ConnState func(net.Conn, ConnState)
ErrorLog *log.Logger
BaseContext func(net.Listener) context.Context
ConnContext func(ctx context.Context, c net.Conn) context.Context
inShutdown atomic.Bool
disableKeepAlives atomic.Bool
nextProtoOnce sync.Once
nextProtoErr error
mu sync.Mutex
listeners map[*net.Listener]struct{}
activeConn map[*conn]struct{}
onShutdown []func()
listenerGroup sync.WaitGroup
}
server.ListenAndServe()
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)
}
net.Listen("tcp", addr) 对地址进行监听,返回 Listener 作为参数传入 Server.Serve() 方法
func (srv *Server) Serve(l net.Listener) error {
...
var tempDelay time.Duration // how long to sleep on accept failure
ctx := context.WithValue(baseCtx, ServerContextKey, srv)
for {
// 循环接受请求
rw, err := l.Accept()
if err != nil {
if srv.shuttingDown() {
return ErrServerClosed
}
if ne, ok := err.(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", err, tempDelay)
time.Sleep(tempDelay)
continue
}
return err
}
connCtx := ctx
if cc := srv.ConnContext; cc != nil {
connCtx = cc(connCtx, rw)
if connCtx == nil {
panic("ConnContext returned nil")
}
}
tempDelay = 0
// 构造一个新的conn,然后开启goroutine去执行本次请求.
c := srv.newConn(rw)
c.setState(c.rwc, StateNew, runHooks) // before Serve can return
go c.serve(connCtx)
}
}
Serve 接受监听器 l 上传入的连接,为每个连接创建一个新的服务 goroutine ,读取请求并然后调用 srv.Handler 来回复它们
执行一个 for 死循环,通过 l.Accept() 来接受请求返回连接 Conn,根据该连接创建对应的 goroutine 来执行 conn.serve(ctx),同时 context 作为参数传入,用于管理这些生成的 goroutine
请求解析
func (c *conn) serve(ctx context.Context) {
...
for {
w, err := c.readRequest(ctx)
...
serverHandler{c.server}.ServeHTTP(w, w.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 赋值,如果是nil,则使用默认的 DefaultServeMux
handler := sh.srv.Handler
if handler == nil {
handler = DefaultServeMux
}
//
if !sh.srv.DisableGeneralOptionsHandler && req.RequestURI == "*" && req.Method == "OPTIONS" {
handler = globalOptionsHandler{}
}
if req.URL != nil && strings.Contains(req.URL.RawQuery, ";") {
var allowQuerySemicolonsInUse atomic.Bool
req = req.WithContext(context.WithValue(req.Context(),
silenceSemWarnContextKey, func() {
allowQuerySemicolonsInUse.Store(true)
}))
defer func() {
if !allowQuerySemicolonsInUse.Load() {
sh.srv.logf("http: URL query contains semicolon, which is no longer a supported separator; parts of the query may be stripped when parsed; see golang.org/issue/25192")
}
}()
}
handler.ServeHTTP(rw, req)
}
handler.ServeHTTP(rw, req)
// 根据request然后去ServeMux中找到属于当前请求的Handler
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)
}
h, _ := mux.Handler(r)
func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) {
// CONNECT requests are not canonicalized.
if r.Method == "CONNECT" {
// If r.URL.Path is /tree and its handler is not registered,
// the /tree -> /tree/ redirect applies to CONNECT requests
// but the path canonicalization does not.
if u, ok := mux.redirectToPathSlash(r.URL.Host, r.URL.Path, r.URL); ok {
return RedirectHandler(u.String(), StatusMovedPermanently), u.Path
}
return mux.handler(r.Host, r.URL.Path)
}
// All other requests have any port stripped and path cleaned
// before passing to mux.handler.
host := stripHostPort(r.Host)
path := cleanPath(r.URL.Path)
// If the given path is /tree and its handler is not registered,
// redirect for /tree/.
if u, ok := mux.redirectToPathSlash(host, path, r.URL); ok {
return RedirectHandler(u.String(), StatusMovedPermanently), u.Path
}
if path != r.URL.Path {
_, pattern = mux.handler(host, path)
u := &url.URL{Path: path, RawQuery: r.URL.RawQuery}
return RedirectHandler(u.String(), StatusMovedPermanently), pattern
}
// 从map中取出自己的对应的handler并返回
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
}
// Find a handler on a handler map given a path string.
// Most-specific (longest) pattern wins.
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, ""
}