type segment struct { // 存储路径段的字面量、通配符名称或特殊符号 s string// literal or wildcard name or "/" for "/{$}". // 标识该段是否为通配符 wild bool // 标识通配符是否匹配多个路径段(仅当wild==true时生效) multi bool// "..." wildcard }
type ServeMux struct { // 读写互斥锁 mu sync.RWMutex // 树形存储 tree routingNode index routingIndex // 路由存储(未来版本可能被移除) patterns []*pattern // TODO(jba): remove if possible // 1.21版本兼容 mux121 serveMux121 // used only when GODEBUG=httpmuxgo121=1 }
// HandleFunc registers the handler function for the given pattern in [DefaultServeMux]. funcHandleFunc(pattern string, handler func(ResponseWriter, *Request)) { if use121 { DefaultServeMux.mux121.handleFunc(pattern, handler) } else { // 这里的HandlerFunc是Handler接口的实现类 DefaultServeMux.register(pattern, HandlerFunc(handler)) } }
1 2 3 4
// DefaultServeMux is the default [ServeMux] used by [Serve]. var DefaultServeMux = &defaultServeMux
// pattern的格式为[METHOD] [HOST]/[PATH] // 在此处根据空格或\t分割为左右两部分 // found表示pattern中是否存在METHOD method, rest, found := s, "", false if i := strings.IndexAny(s, " \t"); i >= 0 { method, rest, found = s[:i], strings.TrimLeft(s[i+1:], " \t"), true } if !found { rest = method method = "" } if method != "" && !validMethod(method) { returnnil, fmt.Errorf("invalid method %q", method) } p := &pattern{str: s, method: method}
if found { off = len(method) + 1 } // 根据"/"分割为host和path两部分 i := strings.IndexByte(rest, '/') if i < 0 { returnnil, errors.New("host/path missing /") } p.host = rest[:i] rest = rest[i:] // 检测host段中是否存在"{" // 因为path中存在类似"{name}", "{name...}", or "{$}"这样的特殊匹配 // 所以如果host段中出现"{"符号的话,就可以认为出现了将path划分到了host段这样的异常情况 if j := strings.IndexByte(p.host, '{'); j >= 0 { off += j returnnil, errors.New("host contains '{' (missing initial '/'?)") } // At this point, rest is the path. off += i
// An unclean path with a method that is not CONNECT can never match, // because paths are cleaned before matching. // 这里的clean path指的是规范的path,不存在"."或".."这样的元素 if method != "" && method != "CONNECT" && rest != cleanPath(rest) { returnnil, errors.New("non-CONNECT pattern with unclean path can never match") }
seenNames := map[string]bool{} // remember wildcard names to catch dups forlen(rest) > 0 { // Invariant: rest[0] == '/'. rest = rest[1:] off = len(s) - len(rest) // 匹配最后rest=="/"的情况 iflen(rest) == 0 { // Trailing slash. p.segments = append(p.segments, segment{wild: true, multi: true}) break } i := strings.IndexByte(rest, '/') if i < 0 { i = len(rest) } var seg string seg, rest = rest[:i], rest[i:] if i := strings.IndexByte(seg, '{'); i < 0 { // Literal. seg = pathUnescape(seg) p.segments = append(p.segments, segment{s: seg}) } else { // Wildcard. if i != 0 { returnnil, errors.New("bad wildcard segment (must start with '{')") } if seg[len(seg)-1] != '}' { returnnil, errors.New("bad wildcard segment (must end with '}')") } name := seg[1 : len(seg)-1] if name == "$" { iflen(rest) != 0 { returnnil, errors.New("{$} not at end") } p.segments = append(p.segments, segment{s: "/"}) break } name, multi := strings.CutSuffix(name, "...") if multi && len(rest) != 0 { returnnil, errors.New("{...} wildcard not at end") } if name == "" { returnnil, errors.New("empty wildcard") } if !isValidWildcardName(name) { returnnil, fmt.Errorf("bad wildcard name % q", name) } if seenNames[name] { returnnil, fmt.Errorf("duplicate wildcard name %q", name) } seenNames[name] = true p.segments = append(p.segments, segment{s: name, wild: true, multi: multi}) } } return p, nil }
func(idx *routingIndex) possiblyConflictingPatterns(pat *pattern, f func(*pattern)error) (err error) { // Terminology: // dollar pattern: one ending in "{$}" // multi pattern: one ending in a trailing slash or "{x...}" wildcard // ordinary pattern: neither of the above
// 对传入apply方法的patterns都调用f函数,遇到error时返回 apply := func(pats []*pattern)error { if err != nil { return err } for _, p := range pats { err = f(p) if err != nil { return err } } returnnil }
// 检查multi pattern和ordinary pattern的情况 // 寻找与pat在某个位置有相同字面量或者通配符的pattern var lmin, wmin []*pattern min := math.MaxInt hasLit := false for i, seg := range pat.segments { if seg.multi { break// 如果遍历到多段通配符,则说明后续被通配符覆盖,停止遍历 } if !seg.wild { hasLit = true // 获取该位置的字面量模式和通配符模式 lpats := idx.segments[routingIndexKey{s: seg.s, pos: i}] wpats := idx.segments[routingIndexKey{s: "", pos: i}] // 获取冲突可能性最小的位置 if sum := len(lpats) + len(wpats); sum < min { lmin = lpats wmin = wpats min = sum } } } if hasLit { apply(lmin) // 检查存在相同字面量segment的模式 apply(wmin) // 检查存在相同通配符segment的模式 return err }
// 检查pat全路径segment都为通配符的情况 // 无字面量可索引,扫描所有已注册的pattern for _, pats := range idx.segments { apply(pats) } return err }
简单来说,在 possiblyConflictingPatterns 方法中,可以快速找出所有可能与给定路由模式 pat 冲突的已注册模式。它的核心思想是通过预置的索引(segments 和 multis)缩小检查范围,避免遍历所有模式,从而提升性能。这里传入的 f 函数为一个匿名函数:
1 2 3 4 5 6 7 8
func(pat2 *pattern)error { if pat.conflictsWith(pat2) { d := describeConflict(pat, pat2) return fmt.Errorf("pattern %q (registered at %s) conflicts with pattern %q (registered at %s):\n%s", pat, pat.loc, pat2, pat2.loc, d) }
// addPattern adds a pattern and its associated Handler to the tree // at root. func(root *routingNode) addPattern(p *pattern, h Handler) { // First level of tree is host. n := root.addChild(p.host) // Second level of tree is method. n = n.addChild(p.method) // Remaining levels are path. n.addSegments(p.segments, p, h) }
// 采用递归的方式向树中加入节点 // 在叶子结点中保存当前的pattern和handler func(n *routingNode) addSegments(segs []segment, p *pattern, h Handler) { iflen(segs) == 0 { n.set(p, h) return } seg := segs[0] if seg.multi { iflen(segs) != 1 { panic("multi wildcard not last") } c := &routingNode{} n.multiChild = c c.set(p, h) } elseif seg.wild { n.addChild("").addSegments(segs[1:], p, h) } else { n.addChild(seg.s).addSegments(segs[1:], p, h) } }
// addChild adds a child node with the given key to n // if one does not exist, and returns the child. func(n *routingNode) addChild(key string) *routingNode { if key == "" { if n.emptyChild == nil { n.emptyChild = &routingNode{} } return n.emptyChild } if c := n.findChild(key); c != nil { return c } c := &routingNode{} n.children.add(key, c) return c }
if w == nil { // Happens in test hook. returnfalse }
// If IdleConnTimeout is set, calculate the oldest // persistConn.idleAt time we're willing to use a cached idle // conn. var oldTime time.Time if t.IdleConnTimeout > 0 { oldTime = time.Now().Add(-t.IdleConnTimeout) }
if list, ok := t.idleConn[w.key]; ok { stop := false delivered := false forlen(list) > 0 && !stop { // 取出最近使用的(队尾)的连接 pconn := list[len(list)-1]
// 检查这个连接是否已经被保存太久了 tooOld := !oldTime.IsZero() && pconn.idleAt.Round(0).Before(oldTime) if tooOld { // 如果太老了,则起一个goroutine进行cleanup go pconn.closeConnIfStillIdle() } if pconn.isBroken() || tooOld { // 如果这个连接已经被persistConn.readLoop方法标记为broken了但是还没有被remove // 或者这个连接已经太老了 // 则忽略,寻找下一个 list = list[:len(list)-1] continue } // 找到后可用的连接后,调用wantConn.tryDeliver方法将其发送到result channel delivered = w.tryDeliver(pconn, nil, pconn.idleAt) if delivered { if pconn.alt != nil { // HTTP/2: multiple clients can share pconn. // Leave it in the list. } else { // HTTP/1: only one client can use pconn. // Remove it from the list. t.idleLRU.remove(pconn) list = list[:len(list)-1] } } stop = true } // 更新连接池 iflen(list) > 0 { t.idleConn[w.key] = list } else { delete(t.idleConn, w.key) } if stop { return delivered } }
// Register to receive next connection that becomes idle. // 走到这里就代表没有获取到可复用的连接 // 重新加入到等待队列中 if t.idleConnWait == nil { t.idleConnWait = make(map[connectMethodKey]wantConnQueue) } q := t.idleConnWait[w.key] q.cleanFrontNotWaiting() q.pushBack(w) t.idleConnWait[w.key] = q returnfalse }
if t.MaxConnsPerHost <= 0 { t.startDialConnForLocked(w) return }
if n := t.connsPerHost[w.key]; n < t.MaxConnsPerHost { if t.connsPerHost == nil { t.connsPerHost = make(map[connectMethodKey]int) } t.connsPerHost[w.key] = n + 1 t.startDialConnForLocked(w) return }