简介
之前介绍过sigs.k8s.io controller-runtime系列之十一 finalizer分析sigs.k8s.io controller-runtime-finalizer 。
本文主要介绍pkg/health的源码分析。
目录结构
- health.go
- 函数
// getExcludedChecks 提取要从查询参数中排除的健康检查名称。 func getExcludedChecks(r *http.Request) sets.String { checks, found := r.URL.Query()["exclude"] if found { return sets.NewString(checks...) } return sets.NewString() } // formatQuoted 返回健康检查名称的格式化字符串并且保留传入的顺序。 func formatQuoted(names ...string) string { quoted := make([]string, 0, len(names)) for _, name := range names { quoted = append(quoted, fmt.Sprintf("%q", name)) } return strings.Join(quoted, ",") } // writeStatusAsText 以我们从 Kubernetes 复制的某种半任意定制的文本格式写出给定的检查状态。 // writeStatusAsText 在失败时总是详细的,并且可以使用给定的参数强制在成功时变得详细。 func writeStatusesAsText(resp http.ResponseWriter, parts []checkStatus, unknownExcludes sets.String, failed, forceVerbose bool) { resp.Header().Set("Content-Type", "text/plain; charset=utf-8") resp.Header().Set("X-Content-Type-Options", "nosniff") // 先写状态码 if failed { resp.WriteHeader(http.StatusInternalServerError) } else { resp.WriteHeader(http.StatusOK) } // forceVerbose=false且成功,resp简单的响应 if !failed && !forceVerbose { fmt.Fprint(resp, "ok") return } // 我们总是在失败时详细,所以从现在开始,我们保证响应时详细的 for _, checkOut := range parts { switch { case checkOut.excluded: fmt.Fprintf(resp, "[+]%s excluded: ok\n", checkOut.name) case checkOut.healthy: fmt.Fprintf(resp, "[+]%s ok\n", checkOut.name) default: fmt.Fprintf(resp, "[-]%s failed: reason withheld\n", checkOut.name) } } // req参数中设置的排除check项目在该Handler总不匹配的项目 if unknownExcludes.Len() > 0 { fmt.Fprintf(resp, "warn: some health checks cannot be excluded: no matches for %s\n", formatQuoted(unknownExcludes.List()...)) } if failed { log.Info("healthz check failed", "statuses", parts) fmt.Fprintf(resp, "healthz check failed\n") } else { fmt.Fprint(resp, "healthz check passed\n") } }
- Handler结构体
// Handler 是一个 http.Handler,它将给定Checker的结果聚合到根路径,并支持在Checker名称的子路径上调用单个检查器。 // 即时添加Checker不是线程安全的——使用包装器。 type Handler struct { Checks map[string]Checker } // 聚合Handler中所有Checker的checkStatus func (h *Handler) serveAggregated(resp http.ResponseWriter, req *http.Request) { failed := false // 获取req请求参数中不需要check的Checker名称 excluded := getExcludedChecks(req) parts := make([]checkStatus, 0, len(h.Checks)) // 计算结果,遍历h.Checks for checkName, check := range h.Checks { // 检查是否存在于我们已经指定要排除检查名称 if excluded.Has(checkName) { // 从排除检查名称中删除(较少遍历项) excluded.Delete(checkName) // 追加check结果healthy: true, excluded: true parts = append(parts, checkStatus{name: checkName, healthy: true, excluded: true}) continue } // check出错 if err := check(req); err != nil { log.V(1).Info("healthz check failed", "checker", checkName, "error", err) // 追加check结果healthy: false parts = append(parts, checkStatus{name: checkName, healthy: false}) // 总的检查结果failed置为true failed = true } else { // 追加check结果healthy: true parts = append(parts, checkStatus{name: checkName, healthy: true}) } } // 如果h.Checks长度为0,则追加检查结果name: "ping", healthy: true if len(h.Checks) == 0 { parts = append(parts, checkStatus{name: "ping", healthy: true}) } for _, c := range excluded.List() { log.V(1).Info("cannot exclude health check, no matches for it", "checker", c) } // 以检查结果中每项的name长度升序排列 sort.Slice(parts, func(i, j int) bool { return parts[i].name < parts[j].name }) // 根据req的参数verbose来响应结果 _, forceVerbose := req.URL.Query()["verbose"] // 以特定的文本格式响应 writeStatusesAsText(resp, parts, excluded, failed, forceVerbose) } // 实现了http.Handler func (h *Handler) ServeHTTP(resp http.ResponseWriter, req *http.Request) { // 修正req的urlpath reqPath := req.URL.Path if reqPath == "" || reqPath[0] != '/' { reqPath = "/" + reqPath } // 1. 用一个斜线代替多个斜线。 // 2. 消除每个 .路径名元素(当前目录)。 // 3. 消除每个内部 .. 路径名元素(父目录) // 以及它之前的非 .. 元素。 // 4. 消除以根路径开头的 .. 元素: // 即,将路径开头的“/..”替换为“/”。 reqPath = path.Clean(reqPath) // 如果reqPath="/",根端点聚合除了Excluded所有的Check if reqPath == "/" { h.serveAggregated(resp, req) return } // 默认Ping检查(如果没有其他内容) if len(h.Checks) == 0 && reqPath[1:] == "ping" { CheckHandler{Checker: Ping}.ServeHTTP(resp, req) return } // 判断reqPath(忽略/)在map中是否存在 checkName := reqPath[1:] checker, known := h.Checks[checkName] if !known { http.NotFoundHandler().ServeHTTP(resp, req) return } // 单一的checker检查 CheckHandler{Checker: checker}.ServeHTTP(resp, req) }
- checkStatus结构体 保存特定检查的输出
type checkStatus struct { name string healthy bool excluded bool }
- CheckHandler结构体及其方法
// CheckHandler 是一个 http.Handler,它基于其检查器在根路径提供健康检查端点 。 type CheckHandler struct { Checker } // Checker 知道如何执行健康检查。 type Checker func(req *http.Request) error // 默认的Ping, 会自动返回 true。 var Ping Checker = func(_ *http.Request) error { return nil } func (h CheckHandler) ServeHTTP(resp http.ResponseWriter, req *http.Request) { // 自定义实现的Checker if err := h.Checker(req); err != nil { http.Error(resp, fmt.Sprintf("internal server error: %v", err), http.StatusInternalServerError) } else { fmt.Fprint(resp, "ok") } }