apiserver是如何实现委托模式的(delegation pattern)?
首先需要明白http.Handler是什么? http.Handler就是一个非常简单的接口,处理一个请求,返回一个响应。
// A Handler responds to an HTTP request.
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}
pathHandler
pathHandler是一个小而精简的实现类,很容易分析:
// pathHandler is an http.Handler that will satisfy requests first by exact match, then by prefix,
// then by notFoundHandler
type pathHandler struct {
// muxName is used for logging so you can trace requests through
muxName string
// 处理精准匹配,比如/apis/get/pods
// pathToHandler is a map of exactly matching request to its handler
pathToHandler map[string]http.Handler
// 用于前缀匹配,在精准匹配没有匹配到,使用前缀匹配
// this has to be sorted by most slashes then by length
prefixHandlers []prefixHandler
// 如果精准匹配和前缀匹配都失败,则使用这个http.Handler进行处理
// notFoundHandler is the handler to use for satisfying requests with no other match
notFoundHandler http.Handler
}
// prefixHandler holds the prefix it should match and the handler to use
type prefixHandler struct {
// prefix is the prefix to test for a request match
prefix string
// handler is used to satisfy matching requests
handler http.Handler
}
代理模式就是这样实现的, 比如pathToHandler和prefixHandlers是属于api-server的,假如匹配不到,就会转发到notFoundHandler,而notFoundHandler属于apiextensions-server.
// ServeHTTP makes it an http.Handler
func (h *pathHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// 处理精准匹配,比如/apis/get/pods
if exactHandler, ok := h.pathToHandler[r.URL.Path]; ok {
klog.V(5).Infof("%v: %q satisfied by exact match", h.muxName, r.URL.Path)
exactHandler.ServeHTTP(w, r)
return
}
// 用于前缀匹配,在精准匹配没有匹配到,使用前缀匹配
for _, prefixHandler := range h.prefixHandlers {
if strings.HasPrefix(r.URL.Path, prefixHandler.prefix) {
klog.V(5).Infof("%v: %q satisfied by prefix %v", h.muxName, r.URL.Path, prefixHandler.prefix)
prefixHandler.handler.ServeHTTP(w, r)
return
}
}
klog.V(5).Infof("%v: %q satisfied by NotFoundHandler", h.muxName, r.URL.Path)
// 如果精准匹配和前缀匹配都失败,则使用委托模式中的下一级的被代理者的http.Handler进行处理
h.notFoundHandler.ServeHTTP(w, r)
}
PathRecorderMux
PathRecorderMux相当于并发安全版的pathHandler.
// PathRecorderMux wraps a mux object and records the registered exposedPaths.
type PathRecorderMux struct {
// name is used for logging so you can trace requests through
name string
lock sync.Mutex
notFoundHandler http.Handler
pathToHandler map[string]http.Handler
prefixToHandler map[string]http.Handler
// mux stores a pathHandler and is used to handle the actual serving.
// Turns out, we want to accept trailing slashes, BUT we don't care about handling
// everything under them. This does exactly matches only unless its explicitly requested to
// do something different
mux atomic.Value
// exposedPaths is the list of paths that should be shown at /
exposedPaths []string
// pathStacks holds the stacks of all registered paths. This allows us to show a more helpful message
// before the "http: multiple registrations for %s" panic.
pathStacks map[string]string
}
可以发现PathRecorderMux和pathHandler的字段大致是相同的,只是内部有一个互斥锁sync.Mutex和存储pathHandler的字段mux atomic.Value。
实际上这两个对象相同字段的数据是一样的。
比如删除一个path:
// Unregister removes a path from the mux.
func (m *PathRecorderMux) Unregister(path string) {
// 加锁
m.lock.Lock()
// 退出时,解锁
defer m.lock.Unlock()
// PathRecorderMux删除自身相关path
delete(m.pathToHandler, path)
delete(m.prefixToHandler, path)
delete(m.pathStacks, path)
for i := range m.exposedPaths {
if m.exposedPaths[i] == path {
m.exposedPaths = append(m.exposedPaths[:i], m.exposedPaths[i+1:]...)
break
}
}
// 同步内部的对象pathHandler
m.refreshMuxLocked()
}
可以发现m.refreshMuxLocked() 只不过是创建一个pathHandler,将外部的数据拷贝到内部。
// refreshMuxLocked creates a new mux and must be called while locked. Otherwise the view of handlers may
// not be consistent
func (m *PathRecorderMux) refreshMuxLocked() {
newMux := &pathHandler{
muxName: m.name,
pathToHandler: map[string]http.Handler{},
prefixHandlers: []prefixHandler{},
notFoundHandler: http.NotFoundHandler(),
}
if m.notFoundHandler != nil {
newMux.notFoundHandler = m.notFoundHandler
}
for path, handler := range m.pathToHandler {
newMux.pathToHandler[path] = handler
}
keys := sets.StringKeySet(m.prefixToHandler).List()
sort.Sort(sort.Reverse(byPrefixPriority(keys)))
for _, prefix := range keys {
newMux.prefixHandlers = append(newMux.prefixHandlers, prefixHandler{
prefix: prefix,
handler: m.prefixToHandler[prefix],
})
}
m.mux.Store(newMux)
}
但是如果处理http请求的话,还是使用内部的pathHandler:
// ServeHTTP makes it an http.Handler
func (m *PathRecorderMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
m.mux.Load().(*pathHandler).ServeHTTP(w, r)
}
那为什么还要维持内外结构体维持两份相同的数据呢? 这个问题搞不懂。