<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      Prometheus源碼專題【左揚精講】—— 監控系統 Prometheus 3.4.0 源碼解析:HTTP 路由及二次開發調試

      Prometheus源碼專題【左揚精講】—— 監控系統 Prometheus 3.4.0 源碼解析:HTTP 路由及二次開發調試

      路由相關的具體實現:

      https://github.com/prometheus/prometheus/blob/v3.4.0/web/web.go  

      API v1 的具體路由注冊實現:

      https://github.com/prometheus/prometheus/blob/v3.4.0/web/web.go#L669-722
      https://github.com/prometheus/prometheus/blob/v3.4.0/web/web.go#L354-430
      https://github.com/prometheus/prometheus/blob/v3.4.0/web/web.go#L61-138
      https://github.com/prometheus/prometheus/blob/v3.4.0/web/web.go#L427-481
      https://github.com/prometheus/prometheus/blob/v3.4.0/web/web.go#L301-358
      https://github.com/prometheus/prometheus/blob/v3.4.0/web/web.go#L474-540
      https://github.com/prometheus/prometheus/blob/v3.4.0/web/api/v1/api.go#L371-388
      https://github.com/prometheus/prometheus/blob/v3.4.0/web/web.go#L536-609
      https://github.com/prometheus/prometheus/blob/v3.4.0/storage/remote/read_handler.go#L61-115
      https://github.com/prometheus/prometheus/blob/v3.4.0/web/web.go#L605-672
      https://github.com/prometheus/prometheus/blob/v3.4.0/storage/remote/write_handler.go#L621-660
      https://github.com/prometheus/prometheus/blob/v3.4.0/web/web.go#L180-254

      查詢處理器實現(web/api/v1):

      https://github.com/prometheus/prometheus/blob/v3.4.0/web/api/v1/api.go 

      聯邦查詢處理器(web):

      https://github.com/prometheus/prometheus/blob/v3.4.0/web/federate.go#L51-106
      https://github.com/prometheus/prometheus/blob/v3.4.0/web/federate.go#L103-179
      https://github.com/prometheus/prometheus/blob/v3.4.0/web/web.go#L301-358
      https://github.com/prometheus/prometheus/blob/v3.4.0/web/api/v1/api.go#321-388
      https://github.com/prometheus/prometheus/blob/v3.4.0/web/federate.go#L0-55
      https://github.com/prometheus/prometheus/blob/v3.4.0/web/web.go#L180-254
      https://github.com/prometheus/prometheus/blob/v3.4.0/web/web.go#L427-481
      https://github.com/prometheus/prometheus/blob/v3.4.0/web/api/v1/api.go#438-511
      https://github.com/prometheus/prometheus/blob/v3.4.0/web/federate.go#L199-258
      https://github.com/prometheus/prometheus/blob/v3.4.0/web/api/v1/api.go#179-235
      https://github.com/prometheus/prometheus/blob/v3.4.0/web/api/v1/api.go#264-325
      https://github.com/prometheus/prometheus/blob/v3.4.0/web/federate.go#L253-313

              在高并發監控場景中,HTTP 路由選擇器的分發效率直接決定了 Prometheus Web 服務的響應能力。當每秒處理數千甚至數萬請求時,路由匹配的速度將成為系統性能的關鍵瓶頸之一。

              本文將從源碼出發,完整拆解 Prometheus 3.4.0 的 HTTP 路由架構,重點分析 GET/POST 方法的前綴樹實現,并探討其高性能設計的核心邏輯。

      一、分層設計的核心邏輯

      Prometheus 的 HTTP 路由系統采用 "主路由 + 子路由" 的分層架構,通過職責拆分實現高內聚低耦合。這種設計既保證了靜態資源、Web UI 與 API 接口的隔離,又通過中間件機制實現了功能的復用。

      1.1、核心組件與依賴

      Prometheus 并未使用第三方路由庫,而是基于自定義的 topicroutetopic 包實現路由功能,核心依賴如下:

          • route.Router:路由核心結構體,維護路徑與處理器的映射關系
          • 中間件機制:通過 WithInstrumentation 注入監控、日志、路徑重寫等通用邏輯
          • http.ServeMux:基礎路由分發器,用于掛載主路由與 API 子路由
          • 處理器函數:每個路由對應獨立的業務處理函數(如查詢、聯邦、版本查詢等)

      1.2、初始化流程 —— 從創建到掛載

      路由系統的初始化在 web.New() 函數(https://github.com/prometheus/prometheus/blob/v3.4.0/web/web.go)中完成,核心步驟分為三步:

      1.2.1、主路由創建(初始化帶監控中間件的路由,用于處理靜態資源和頁面請求)

      https://github.com/prometheus/prometheus/blob/v3.4.0/web/web.go#L311

      router := route.New().
          WithInstrumentation(m.instrumentHandler).  // 注入請求監控
          WithInstrumentation(setPathWithPrefix(""))  // 路徑前綴處理 

      1.2.2、API v1 子路由創建(獨立創建 API 路由,避免與主路由邏輯混淆)

      https://github.com/prometheus/prometheus/blob/v3.4.0/web/web.go#L690

      av1 := route.New().
          WithInstrumentation(h.metrics.instrumentHandlerWithPrefix("/api/v1")).  // API 專屬監控
          WithInstrumentation(setPathWithPrefix(apiPath + "/v1"))  // API 路徑前綴
      h.apiV1.Register(av1)  // 注冊 API 端點

      1.2.3、路由掛載(將主路由和 API 子路由掛載到基礎 ServeMux)

      https://github.com/prometheus/prometheus/blob/v3.4.0/web/web.go#L682

      mux := http.NewServeMux()
      mux.Handle("/", h.router)  // 主路由掛載到根路徑
      mux.Handle(apiPath+"/v1/", http.StripPrefix(apiPath+"/v1", av1))  // API 子路由掛載

      二、按功能劃分的路由注冊機制

      Prometheus 按 "功能模塊" 劃分路由,每個模塊對應獨立的注冊邏輯。

      通過源碼分析,可將路由分為四大類:靜態資源路由Web UI 路由API v1 路由聯邦查詢路由

      2.1、主路由注冊(靜態資源和頁面路由)

      主路由(router)的注冊邏輯全部包含在 https://github.com/prometheus/prometheus/blob/v3.4.0/web/web.go 的 New 函數中,該函數在初始化 Hander 結構體時,同步完成了處理靜態資源、Web UI 入口、健康檢查、生命周期等路由的注冊。

      核心路由及對應源代碼如下:

      路由路徑請求方法處理器函數 / 核心邏輯功能描述源碼文件及行號(v3.4.0)
      topic/topic GET 重定向到首頁(根據配置指向 topic/querytopic/topic/graphtopic/topic/agenttopic) 根路徑統一跳轉,適配新 / 舊 UI 及 Agent 模式 web/web.go L415-L417
      topic/graphtopic GET 重定向到 topic/querytopic(僅非舊 UI 模式,topic!o.UseOldUItopic 為 true 時生效) 新 UI 下統一查詢入口,替代舊 topic/graphtopic 頁面 web/web.go L419-L422
      topic/classic/static/*filepathtopic GET 靜態文件服務(topicserver.StaticFileServer(ui.Assets)topic),重寫路徑為 topic/static/*filepathtopic 兼容舊控制臺模板依賴的靜態資源(如 JS/CSS) web/web.go L424-L430
      topic/versiontopic GET topich.versiontopic(返回 JSON 格式的版本信息) 暴露 Prometheus 版本、分支、編譯時間等元數據 web/web.go L432
      topic/metricstopic GET topicpromhttp.Handler().ServeHTTPtopic 暴露 Prometheus 自身的監控指標(如請求量、耗時) web/web.go L433
      topic/consoles/*filepathtopic GET topicreadyf(h.consoles)topic(需服務就緒,返回控制臺模板頁面) 提供自定義控制臺模板的訪問入口(如自定義監控面板) web/web.go L440
      topic/favicon.svgtopic GET 靜態文件服務(topicui.Assetstopic),路徑重寫為 topicreactAssetsRoot + "/favicon.svg"topic 提供瀏覽器標簽頁圖標 web/web.go L474-L486(同 topic/favicon.icotopic/topic/manifest.jsontopic)
      topic/favicon.icotopic GET 同上(靜態文件服務,適配不同瀏覽器圖標需求) 兼容舊瀏覽器的圖標訪問需求 web/web.go L474-L486
      topic/manifest.jsontopic GET 同上(靜態文件服務,返回 PWA 應用清單) 支持將 Web UI 作為漸進式 Web 應用(PWA)添加到桌面 / 手機 web/web.go L474-L486
      topic/assets/*filepathtopic GET 靜態文件服務(topicui.Assetstopic),路徑重寫為 topicreactAssetsRoot + "/assets/*filepath"topic 加載新 UI(Mantine 框架)的靜態資源(如組件樣式、圖片) web/web.go L488-L494(非舊 UI 模式)
      topic/static/*filepathtopic GET 靜態文件服務(topicui.Assetstopic),路徑重寫為 topicreactAssetsRoot + "/static/*filepath"topic 加載舊 UI(React 舊框架)的靜態資源(僅 topico.UseOldUItopic 為 true 時生效) web/web.go L488-L494
      topic/user/*filepathtopic GET topicroute.FileServe(o.UserAssetsPath)topic(用戶自定義靜態資源服務) 提供用戶本地自定義靜態資源的訪問入口(如自定義圖片、模板) web/web.go L496-L498
      topic/-/quittopic POST/PUT topich.quittopic(生命周期開啟時)/ 403 響應(生命周期關閉時) 觸發 Prometheus 服務優雅退出 web/web.go L500-L514
      topic/-/quittopic GET 返回 405 響應(提示 “僅支持 POST/PUT 方法”) 限制非法請求方法,保證接口安全性 web/web.go L516-L519
      topic/-/reloadtopic POST/PUT topich.reloadtopic(生命周期開啟時)/ 403 響應(生命周期關閉時) 觸發 Prometheus 配置重載(如刷新采集規則、告警規則) web/web.go L500-L514
      topic/-/reloadtopic GET 返回 405 響應(提示 “僅支持 POST/PUT 方法”) 限制非法請求方法,保證接口安全性 web/web.go L521-L523
      topic/debug/*subpathtopic GET/POST topicserveDebugtopic(內部轉發到 pprof 調試接口) 提供 Go 程序標準調試接口(如 topic/debug/pprof/profiletopic 采集性能樣本) web/web.go L524-L525
      topic/-/healthytopic GET 返回 200 狀態碼 + “XXX is Healthy” 文本 健康檢查接口(判斷服務是否啟動,不依賴業務就緒) web/web.go L548-L551
      topic/-/healthytopic HEAD 返回 200 狀態碼(無響應體) 輕量健康檢查,適配監控系統 HEAD 請求優化 web/web.go L552-L555
      topic/-/readytopic GET topicreadyf(...)topic(服務就緒時返回 200,否則返回 503) 就緒檢查接口(判斷服務是否完成初始化,可處理業務請求) web/web.go L557-L560
      topic/-/readytopic HEAD topicreadyf(...)topic(同上,無響應體) 輕量就緒檢查,適配監控系統 HEAD 請求優化 web/web.go L561-L564

      以下是對該 Newtopic 函數(Prometheus v3.4.0 web 模塊路由初始化核心邏輯)的逐行注釋,重點標注路由注冊、配置依賴、處理器綁定等關鍵邏輯:

      https://github.com/prometheus/prometheus/blob/v3.4.0/web/web.go#L305

      // New 初始化一個新的 web Handler(核心功能:初始化 Handler 結構體 + 注冊所有 web 路由)
      func New(logger *slog.Logger, o *Options) *Handler {
      	// 日志器容錯:若傳入 logger 為 nil,使用空日志器(避免空指針)
      	if logger == nil {
      		logger = promslog.NewNopLogger()
      	}
      
      	// 初始化監控指標:基于傳入的注冊器(Registerer)創建 metrics 實例,用于路由請求的指標統計
      	m := newMetrics(o.Registerer)
      	// 初始化路由管理器:
      	// 1. 綁定指標監控中間件(m.instrumentHandler,統計路由請求耗時、成功/失敗數等)
      	// 2. 綁定路徑前綴設置中間件(setPathWithPrefix(""),初始化路徑前綴為空)
      	router := route.New().
      		WithInstrumentation(m.instrumentHandler).
      		WithInstrumentation(setPathWithPrefix(""))
      
      	// 獲取當前工作目錄(CWD):用于后續記錄服務運行路徑,失敗時記錄錯誤信息
      	cwd, err := os.Getwd()
      	if err != nil {
      		cwd = "<error retrieving current working directory>"
      	}
      
      	// 初始化 Handler 核心結構體:聚合所有依賴(日志、監控、路由、配置、存儲等)
      	h := &Handler{
      		logger: logger,                  // 日志器
      		gatherer: o.Gatherer,            // 指標采集器(用于暴露 /metrics)
      		metrics:  m,                     // 自定義監控指標實例
      		router:      router,             // 路由管理器
      		quitCh:      make(chan struct{}),// 服務退出信號通道
      		reloadCh:    make(chan chan error),// 配置重載信號通道(帶錯誤返回)
      		options:     o,                  // 傳入的 web 配置選項
      		versionInfo: o.Version,          // 服務版本信息
      		birth:       time.Now().UTC(),   // 服務啟動時間(UTC 時區)
      		cwd:         cwd,                // 服務運行目錄
      		flagsMap:    o.Flags,            // 命令行參數映射
      
      		// 核心業務依賴注入(Prometheus 核心功能模塊)
      		context:         o.Context,         // 根上下文(用于服務生命周期管理)
      		scrapeManager:   o.ScrapeManager,   // 采集管理器(管理目標采集任務)
      		ruleManager:     o.RuleManager,     // 規則管理器(管理告警/記錄規則)
      		queryEngine:     o.QueryEngine,     // 查詢引擎(處理 PromQL 查詢)
      		lookbackDelta:   o.LookbackDelta,   // 查詢回溯時間窗口
      		storage:         o.Storage,         // 主存儲(長期指標存儲)
      		localStorage:    o.LocalStorage,    // 本地存儲(短期/臨時指標)
      		exemplarStorage: o.ExemplarStorage, // 樣本存儲(關聯指標的原始樣本)
      		notifier:        o.Notifier,        // 告警通知器(發送告警到 Alertmanager)
      
      		now: model.Now, // 時間獲取函數(默認當前時間,便于測試 mock)
      	}
      	// 設置服務初始狀態為「未就緒」(NotReady),就緒后通過其他邏輯更新
      	h.SetReady(NotReady)
      
      	// 初始化 API v1 所需的「資源檢索器工廠函數」:
      	// 作用是為 API 提供統一的資源訪問入口,解耦 API 與具體模塊的依賴
      	factorySPr := func(_ context.Context) api_v1.ScrapePoolsRetriever { return h.scrapeManager } // 采集池檢索器(API 查采集池)
      	factoryTr := func(_ context.Context) api_v1.TargetRetriever { return h.scrapeManager }     // 采集目標檢索器(API 查采集目標)
      	factoryAr := func(_ context.Context) api_v1.AlertmanagerRetriever { return h.notifier }    // Alertmanager 檢索器(API 查告警接收器)
      	FactoryRr := func(_ context.Context) api_v1.RulesRetriever { return h.ruleManager }        // 規則檢索器(API 查告警/記錄規則)
      
      	// 初始化 Appendable 存儲:僅當啟用遠程寫入接收(RemoteWriteReceiver)或 OTLP 寫入接收時,使用主存儲
      	var app storage.Appendable
      	if o.EnableRemoteWriteReceiver || o.EnableOTLPWriteReceiver {
      		app = h.storage
      	}
      
      	// 初始化 API v1 實例:綁定所有核心依賴,為后續路由提供 API 能力
      	h.apiV1 = api_v1.NewAPI(
      		h.queryEngine,          // 查詢引擎
      		h.storage,              // 主存儲
      		app,                    // 可追加存儲(遠程寫入用)
      		h.exemplarStorage,      // 樣本存儲
      		factorySPr,             // 采集池檢索器工廠
      		factoryTr,              // 采集目標檢索器工廠
      		factoryAr,              // Alertmanager 檢索器工廠
      		// 配置獲取函數(帶讀鎖,保證配置讀取線程安全)
      		func() config.Config {
      			h.mtx.RLock()         // 加讀鎖(防止配置更新時并發讀取)
      			defer h.mtx.RUnlock() // 釋放讀鎖
      			return *h.config
      		},
      		o.Flags,                // 命令行參數
      		// API 全局 URL 配置(用于生成正確的外部鏈接)
      		api_v1.GlobalURLOptions{
      			ListenAddress: o.ListenAddresses[0], // 監聽地址(第一個)
      			Host:          o.ExternalURL.Host,   // 外部訪問 Host
      			Scheme:        o.ExternalURL.Scheme, // 外部訪問協議(http/https)
      		},
      		h.testReady,            // 就緒狀態檢查函數(API 就緒性校驗)
      		h.options.LocalStorage, // 本地存儲
      		h.options.TSDBDir,      // TSDB 存儲目錄
      		h.options.EnableAdminAPI, // 是否啟用 Admin API(如刪除指標)
      		logger,                 // 日志器
      		FactoryRr,              // 規則檢索器工廠
      		h.options.RemoteReadSampleLimit,    // 遠程讀取樣本數限制
      		h.options.RemoteReadConcurrencyLimit, // 遠程讀取并發數限制
      		h.options.RemoteReadBytesInFrame,   // 遠程讀取單幀字節數限制
      		h.options.IsAgent,       // 是否為 Agent 模式(區別于 Server 模式)
      		h.options.CORSOrigin,    // CORS 跨域配置
      		h.runtimeInfo,           // 運行時信息(如內存、CPU 占用)
      		h.versionInfo,           // 版本信息
      		h.options.NotificationsGetter, // 告警通知查詢器(API 查告警歷史)
      		h.options.NotificationsSub,    // 告警通知訂閱器(API 訂閱告警)
      		o.Gatherer,              // 指標采集器
      		o.Registerer,            // 指標注冊器
      		nil,                     // 預留參數(暫無使用)
      		o.EnableRemoteWriteReceiver, // 是否啟用遠程寫入接收
      		o.AcceptRemoteWriteProtoMsgs, // 是否接受 Protobuf 格式的遠程寫入
      		o.EnableOTLPWriteReceiver,    // 是否啟用 OTLP 寫入接收
      		o.ConvertOTLPDelta,           // 是否將 OTLP Delta 指標轉為 Cumulative
      		o.NativeOTLPDeltaIngestion,   // 是否原生支持 OTLP Delta 指標攝入
      		o.CTZeroIngestionEnabled,     // 是否啟用 CT Zero 指標攝入
      	)
      
      	// 路由前綴配置:若配置了非根路徑前綴(RoutePrefix != "/")
      	if o.RoutePrefix != "/" {
      		// 根路徑重定向:訪問 "/" 時,重定向到配置的前綴路徑(避免 404)
      		router.Get("/", func(w http.ResponseWriter, r *http.Request) {
      			http.Redirect(w, r, o.RoutePrefix, http.StatusFound)
      		})
      		// 為所有后續注冊的路由添加前綴(批量生效)
      		router = router.WithPrefix(o.RoutePrefix)
      	}
      
      	// 首頁路徑配置:根據模式選擇默認首頁
      	homePage := "/query"          // 默認首頁:新 UI 的查詢頁面
      	if o.UseOldUI {
      		homePage = "/graph"       // 舊 UI 模式:首頁為 /graph
      	}
      	if o.IsAgent {
      		homePage = "/agent"       // Agent 模式:首頁為 /agent
      	}
      
      	// 就緒狀態檢查函數:復用 Handler 的 testReady 方法(判斷服務是否就緒)
      	readyf := h.testReady
      
      	// 根路徑重定向:訪問路由前綴根路徑(如 /prometheus/)時,重定向到配置的首頁
      	router.Get("/", func(w http.ResponseWriter, r *http.Request) {
      		// 拼接外部 URL 路徑 + 首頁路徑(保證重定向地址正確)
      		http.Redirect(w, r, path.Join(o.ExternalURL.Path, homePage), http.StatusFound)
      	})
      
      	// 舊 UI 兼容:非舊 UI 模式下,訪問 /graph 重定向到 /query(統一新 UI 入口)
      	if !o.UseOldUI {
      		router.Get("/graph", func(w http.ResponseWriter, r *http.Request) {
      			// 保留原查詢參數(RawQuery),避免參數丟失
      			http.Redirect(w, r, path.Join(o.ExternalURL.Path, "/query?"+r.URL.RawQuery), http.StatusFound)
      		})
      	}
      
      	// React 前端資源根路徑配置:根據 UI 模式選擇資源目錄
      	reactAssetsRoot := "/static/mantine-ui" // 新 UI:Mantine 組件庫資源目錄
      	if h.options.UseOldUI {
      		reactAssetsRoot = "/static/react-app" // 舊 UI:舊 React 應用資源目錄
      	}
      
      	// 舊控制臺兼容路由:為 /classic/static/*filepath 提供靜態資源服務
      	// 用途:舊控制臺庫(如 console_libraries/prom.lib)依賴該路徑下的資源
      	router.Get("/classic/static/*filepath", func(w http.ResponseWriter, r *http.Request) {
      		// 路徑重寫:將 /classic/static/xxx 轉為 /static/xxx(匹配 ui.Assets 中的資源路徑)
      		r.URL.Path = path.Join("/static", route.Param(r.Context(), "filepath"))
      		// 使用靜態文件服務:從 ui.Assets(嵌入式資源)中讀取文件
      		fs := server.StaticFileServer(ui.Assets)
      		fs.ServeHTTP(w, r)
      	})
      
      	// 版本信息路由:GET /version,調用 h.version 處理器返回版本信息
      	router.Get("/version", h.version)
      	// 自身監控指標路由:GET /metrics,使用 promhttp 提供標準 Prometheus 指標暴露
      	router.Get("/metrics", promhttp.Handler().ServeHTTP)
      
      	// 聯邦查詢路由:GET /federate
      	// 1. 就緒校驗:通過 readyf 中間件,未就緒時返回 503
      	// 2. 壓縮處理:使用 httputil.CompressionHandler 壓縮響應(減少網絡傳輸)
      	// 3. 處理器:調用 h.federation 處理聯邦查詢請求
      	router.Get("/federate", readyf(httputil.CompressionHandler{
      		Handler: http.HandlerFunc(h.federation),
      	}.ServeHTTP))
      
      	// 控制臺頁面路由:GET /consoles/*filepath
      	// 1. 就緒校驗:未就緒時拒絕訪問
      	// 2. 處理器:調用 h.consoles 加載控制臺頁面資源(支持自定義控制臺)
      	router.Get("/consoles/*filepath", readyf(h.consoles))
      
      	// React 應用服務函數:動態生成 React 首頁 HTML(替換占位符為實際配置)
      	serveReactApp := func(w http.ResponseWriter, _ *http.Request) {
      		// 拼接 React 首頁 HTML 路徑(從嵌入式資源中讀取)
      		indexPath := reactAssetsRoot + "/index.html"
      		f, err := ui.Assets.Open(indexPath)
      		if err != nil {
      			// 資源讀取失敗:返回 500 并提示錯誤
      			w.WriteHeader(http.StatusInternalServerError)
      			fmt.Fprintf(w, "Error opening React index.html: %v", err)
      			return
      		}
      		// 延遲關閉文件句柄(避免資源泄漏)
      		defer func() { _ = f.Close() }()
      		// 讀取 HTML 文件內容
      		idx, err := io.ReadAll(f)
      		if err != nil {
      			// 內容讀取失敗:返回 500 并提示錯誤
      			w.WriteHeader(http.StatusInternalServerError)
      			fmt.Fprintf(w, "Error reading React index.html: %v", err)
      			return
      		}
      		// 替換 HTML 中的占位符為實際配置(讓前端獲取后端配置)
      		replacedIdx := bytes.ReplaceAll(idx, []byte("CONSOLES_LINK_PLACEHOLDER"), []byte(h.consolesPath())) // 控制臺鏈接
      		replacedIdx = bytes.ReplaceAll(replacedIdx, []byte("TITLE_PLACEHOLDER"), []byte(h.options.PageTitle)) // 頁面標題
      		replacedIdx = bytes.ReplaceAll(replacedIdx, []byte("AGENT_MODE_PLACEHOLDER"), []byte(strconv.FormatBool(h.options.IsAgent))) // 是否為 Agent 模式
      		replacedIdx = bytes.ReplaceAll(replacedIdx, []byte("READY_PLACEHOLDER"), []byte(strconv.FormatBool(h.isReady()))) // 服務就緒狀態
      		replacedIdx = bytes.ReplaceAll(replacedIdx, []byte("LOOKBACKDELTA_PLACEHOLDER"), []byte(model.Duration(h.options.LookbackDelta).String())) // 查詢回溯窗口
      		// 返回替換后的 HTML 內容(前端渲染入口)
      		w.Write(replacedIdx)
      	}
      
      	// React 路由路徑配置:根據 UI 模式選擇前端路由列表
      	reactRouterPaths := newUIReactRouterPaths       // 新 UI 前端路由(如 /query、/alerts)
      	reactRouterServerPaths := newUIReactRouterServerPaths // 新 UI Server 模式路由
      	if h.options.UseOldUI {
      		reactRouterPaths = oldUIReactRouterPaths       // 舊 UI 前端路由
      		reactRouterServerPaths = oldUIReactRouterServerPaths // 舊 UI Server 模式路由
      	}
      
      	// 注冊 React 前端路由:為所有前端路由綁定 serveReactApp 處理器(前端路由復用首頁 HTML)
      	for _, p := range reactRouterPaths {
      		router.Get(p, serveReactApp)
      	}
      
      	//  Agent 模式額外路由:注冊 Agent 模式專屬的前端路由
      	if h.options.IsAgent {
      		for _, p := range reactRouterAgentPaths {
      			router.Get(p, serveReactApp)
      		}
      	} else {
      		//  Server 模式額外路由:注冊 Server 模式專屬的前端路由
      		for _, p := range reactRouterServerPaths {
      			router.Get(p, serveReactApp)
      		}
      	}
      
      	// 根路徑靜態資源路由:為瀏覽器圖標、應用清單提供服務(需根路徑訪問,如 /favicon.ico)
      	for _, p := range []string{"/favicon.svg", "/favicon.ico", "/manifest.json"} {
      		// 拼接資源在嵌入式資產中的實際路徑
      		assetPath := reactAssetsRoot + p
      		router.Get(p, func(w http.ResponseWriter, r *http.Request) {
      			// 路徑重寫:將根路徑資源請求轉為嵌入式資產中的路徑
      			r.URL.Path = assetPath
      			fs := server.StaticFileServer(ui.Assets)
      			fs.ServeHTTP(w, r)
      		})
      	}
      
      	// React 靜態資源目錄配置:根據 UI 模式選擇資源目錄
      	reactStaticAssetsDir := "/assets"  // 新 UI:靜態資源目錄(如 /assets/css、/assets/js)
      	if h.options.UseOldUI {
      		reactStaticAssetsDir = "/static" // 舊 UI:靜態資源目錄(如 /static/css)
      	}
      	// React 靜態資源路由:為 /assets/* 或 /static/* 提供靜態資源服務
      	router.Get(reactStaticAssetsDir+"/*filepath", func(w http.ResponseWriter, r *http.Request) {
      		// 路徑重寫:拼接資產根路徑 + 靜態資源目錄 + 文件路徑(匹配嵌入式資源)
      		r.URL.Path = path.Join(reactAssetsRoot+reactStaticAssetsDir, route.Param(r.Context(), "filepath"))
      		fs := server.StaticFileServer(ui.Assets)
      		fs.ServeHTTP(w, r)
      	})
      
      	// 用戶自定義靜態資源路由:若配置了 UserAssetsPath(用戶本地資源目錄)
      	if o.UserAssetsPath != "" {
      		// 注冊 /user/*filepath 路由,從用戶指定目錄加載靜態資源
      		router.Get("/user/*filepath", route.FileServe(o.UserAssetsPath))
      	}
      
      	// 生命周期 API 配置:若啟用生命周期管理(EnableLifecycle = true)
      	if o.EnableLifecycle {
      		// 服務退出路由:POST/PUT /-/quit,調用 h.quit 觸發服務退出
      		router.Post("/-/quit", h.quit)
      		router.Put("/-/quit", h.quit)
      		// 配置重載路由:POST/PUT /-/reload,調用 h.reload 觸發配置重載
      		router.Post("/-/reload", h.reload

      2.2、API v1 路由注冊(監控核心接口)

      https://github.com/prometheus/prometheus/blob/v3.4.0/web/web.go#L359

      API v1 路由不包含在主路由中,而是通過獨立的 topicapi_v1.APItopictopic(https://github.com/prometheus/prometheus/blob/v3.4.0/web/web.go#L62topictopic)topic 結構體注冊,最終掛載到主 topicServeMuxtopic 上。整個流程分為兩步:

      2.2.1、初始化 API v1 實例

      https://github.com/prometheus/prometheus/blob/v3.4.0/web/web.go#L359

      在 New 函數中,通過 api_v1.NewAPI 初始化 API 處理實例,傳入查詢引擎、存儲、配置等核心依賴,為路由注冊做準備。

      h.apiV1 = api_v1.NewAPI(h.queryEngine, h.storage, app, h.exemplarStorage, factorySPr, factoryTr, factoryAr,
          func() config.Config { /* 配置獲取邏輯 */ },
          o.Flags,
          api_v1.GlobalURLOptions{ /* URL 配置 */ },
          // 其他依賴參數...
      )

      2.2.2、注冊并掛載 API v1 路由

      https://github.com/prometheus/prometheus/blob/v3.4.0/web/web.go#L682-695

      • 第 690-692 行:創建 API v1 專屬路由 av1,并添加監控中間件
      • 第 693 行:調用 h.apiV1.Register(av1) 注冊所有 API 端點
      • 第 695 行:通過 http.StripPrefix 將 API v1 路由掛載到主 ServeMux
      • 第 374-389 行:在 web/api/v1/api.go 中注冊核心監控接口,包括 /query、/query_range、/labels 等

       在 Handler.Run 函數中,創建 API v1 專屬路由(av1 https://github.com/prometheus/prometheus/blob/v3.4.0/web/web.go#L690),調用 h.apiV1.Register(av1) 注冊所有 API 端點(https://github.com/prometheus/prometheus/blob/v3.4.0/web/api/v1/api.go#L374-389),最后通過 http.StripPrefix 掛載到主 ServeMux(https://github.com/prometheus/prometheus/blob/v3.4.0/web/web.go#L690)

      # https://github.com/prometheus/prometheus/blob/v3.4.0/web/web.go#L690
      mux := http.NewServeMux() mux.Handle("/", h.router) apiPath := "/api" if h.options.RoutePrefix != "/" { apiPath = h.options.RoutePrefix + apiPath h.logger.Info("Router prefix", "prefix", h.options.RoutePrefix) } av1 := route.New(). WithInstrumentation(h.metrics.instrumentHandlerWithPrefix("/api/v1")). WithInstrumentation(setPathWithPrefix(apiPath + "/v1")) h.apiV1.Register(av1) mux.Handle(apiPath+"/v1/", http.StripPrefix(apiPath+"/v1", av1))
            • 注冊核心端點:包含 /query//query_range//labels 等監控接口,邏輯在 https://github.com/prometheus/prometheus/blob/v3.4.0/web/api/v1/api.go 的 Register 函數(https://github.com/prometheus/prometheus/blob/v3.4.0/web/api/v1/api.go#L339)中(如 L371-L388 注冊 /query/,/query_range)。
            • 掛載代碼:
              // 源碼位置:web/web.go L611-L620
              av1 := route.New().
                  WithInstrumentation(h.metrics.instrumentHandlerWithPrefix("/api/v1")).
                  WithInstrumentation(setPathWithPrefix(apiPath + "/v1"))
              h.apiV1.Register(av1)
              mux.Handle(apiPath+"/v1/", http.StripPrefix(apiPath+"/v1", av1))

      2.3、聯邦查詢路由(跨實例數據聚合)

      聯邦查詢路由屬于主路由的一部分,并非獨立模塊,其注冊和處理邏輯如下:

      • 路由注冊:在 New 函數中,通過 router.Get("/federate", ...) 注冊,處理器為 h.federation(https://github.com/prometheus/prometheus/blob/v3.4.0/web/web.go#L440-442)。
      • 核心邏輯:h.federation 函數(定義在 https://github.com/prometheus/prometheus/blob/v3.4.0/web/federate.go L51-L106)負責解析 match[] 參數、查詢本地存儲數據,并通過 expfmt 格式返回聚合結果,支持跨 Prometheus 實例的數據拉取。

      三、路由注冊與源碼映射表

              該表基于 https://github.com/prometheus/prometheus/blob/v3.4.0/web/web.go  https://github.com/prometheus/prometheus/blob/v3.4.0/web/api/v1/api.go 核心源碼,按 "主路由(靜態 / 基礎功能)" "API v1 路由(監控核心)" "聯邦查詢路由" 三大類劃分,明確每個路由的路徑、方法、處理器及精確源碼位置,確保與實際代碼完全對齊。

      3.1、主路由注冊,請參考本文 `主路由注冊(靜態資源和頁面路由)`

      3.2、API v1 路由:監控核心功能路由

      API v1 路由通過 api_v1.API 結構體獨立注冊,在 web/handler.go 的 Run 函數中掛載到主 ServeMux,負責處理所有監控核心操作(如查詢、標簽管理、元數據獲取)。

      路由路徑請求方法處理器函數(topicapi_v1.APItopic 內方法)功能描述源碼文件及行號(v3.4.0)
      topic/querytopic GET/POST topicapi.querytopic 即時查詢(獲取單個時間點的監控指標數據,支持 PromQL) web/api/v1/api.go L374-L375
      topic/query_rangetopic GET/POST topicapi.queryRangetopic 范圍查詢(獲取指定時間段內的監控指標數據,支持 PromQL) web/api/v1/api.go L376-L377
      topic/query_exemplarstopic GET/POST topicapi.queryExemplarstopic 樣本查詢(獲取監控指標對應的 exemplar 數據,用于追蹤鏈路關聯) web/api/v1/api.go L378-L379
      topic/format_querytopic GET/POST topicapi.formatQuerytopic 查詢語句格式化(美化 PromQL 語句,便于閱讀和調試) web/api/v1/api.go L381-L382
      topic/parse_querytopic GET/POST topicapi.parseQuerytopic 查詢語句解析(校驗 PromQL 語法正確性,返回解析結果) web/api/v1/api.go L383-L384
      topic/labelstopic GET/POST topicapi.labelNamestopic 標簽名查詢(獲取所有監控指標的標簽鍵(label key)列表) web/api/v1/api.go L386-L387
      topic/label/:name/valuestopic GET/POST topicapi.labelValuestopic 標簽值查詢(獲取指定標簽鍵對應的所有標簽值(label value)列表) web/api/v1/api.go L388
      topic/seriestopic GET/POST topicapi.seriestopic 時間序列查詢(根據標簽篩選條件,獲取匹配的時間序列列表) web/api/v1/api.go L390-L391(源碼中緊隨 topic/label/:name/valuestopic 注冊)
      topic/metadatatopic GET/POST topicapi.metadatatopic 指標元數據查詢(獲取監控指標的類型、幫助信息、標簽等元數據) web/api/v1/api.go L393-L394

      3.2、聯邦查詢路由——跨實例數據聚合路由

      聯邦查詢路由屬于主路由的子集,專門用于跨 Prometheus 實例聚合數據,注冊和處理邏輯獨立于普通 API。

      路由路徑請求方法處理器函數 / 核心邏輯功能描述源碼文件及行號(v3.4.0)
      topic/federatetopic GET topicreadyf(httputil.CompressionHandler{ Handler: http.HandlerFunc(h.federation) })topic 聯邦查詢入口(接收 topicmatch[]topic 參數,返回符合條件的監控指標,支持跨實例聚合) 注冊邏輯:web/web.go L435-L438;處理邏輯:web/federate.go L51-L106

      四、Prometheus 3.4.0 二次開發時路由調試

      在實際部署或二次開發中,路由匹配異常(如請求 404、接口無響應)是常見問題。本文從“日志定位”、“有效性驗證”、“問題排查”三個核心場景出發,提供可直接操作的步驟,幫助快速定位并解決路由相關問題。

      4.1、如何通過日志定位路由匹配過程 

      Prometheus 默認不輸出路由匹配細節,需通過 調整日志級別 和 啟用請求追蹤日志,完整記錄請求從接收、路由匹配到處理器調用的全流程。

      路由匹配的核心日志(如路徑解析、中間件執行)屬于 topicdebugtopic 級別,需通過啟動參數或配置文件開啟(推薦,臨時生效)

      ./prometheus --log.level=debug --config.file=prometheus.yml
      
          • --log.level=debug:將全局日志級別設為 debug,路由相關日志(如 route 包的匹配日志)會被輸出。

      通過配置文件開啟(長期生效)

      # 在 prometheus.yml 中添加日志配置(需 Prometheus 3.0+ 支持):
      
      global:
        scrape_interval: 15s
      log:
        level: debug  # 全局日志級別
        format: logfmt  # 日志格式,logfmt 或 json(json 便于機器解析)  

      開啟 topicdebugtopic 日志后,可通過關鍵字過濾路由相關日志,核心日志類型及含義如下:

      日志關鍵字日志含義示例日志(簡化)
      topicroute matchedtopic 路由匹配成功,包含請求方法、路徑、匹配到的處理器名稱 topiclevel=debug ts=2024-05-20T10:30:00Z caller=route/route.go:123 msg="route matched" method=GET path=/api/v1/query handler=api_v1.querytopic
      topicroute not foundtopic 路由匹配失敗,請求路徑或方法不存在 topiclevel=debug ts=2024-05-20T10:31:00Z caller=route/route.go:135 msg="route not found" method=POST path=/api/v1/unknowntopic
      topicinstrumentHandlertopic 中間件執行日志,包含請求耗時、處理器名稱(監控中間件輸出) topiclevel=debug ts=2024-05-20T10:32:00Z caller=web/metrics.go:45 msg="instrumentHandler" handler=api_v1.query duration=120mstopic
      topichttp server: servingtopic HTTP 服務器接收請求的初始日志,包含客戶端 IP、請求方法、路徑 topiclevel=debug ts=2024-05-20T10:29:00Z caller=web/handler.go:680 msg="http server: serving" client=192.168.1.100:54321 method=GET path=/metricstopic

      4.2、啟用請求追蹤,關聯全鏈路日志

      若需追蹤單個請求的完整鏈路(從接收→路由匹配→中間件→處理器→響應),可通過 HTTP 請求頭傳遞追蹤 ID,并在日志中關聯該 ID:

      4.2.1、客戶端發送請求時添加追蹤頭(如 topicX-Trace-IDtopic)

      curl -H "X-Trace-ID: abc123" http://localhost:9090/api/v1/query?query=up

      4.2.2、修改 Prometheus 日志配置(需二次開發或使用自定義中間件)

      在 withStackTracer 中間件(https://github.com/prometheus/prometheus/blob/v3.4.0/web/web.go L178-L195)中添加追蹤頭日志,示例修改:

      func withStackTracer(h http.Handler, l *slog.Logger) http.Handler {
          return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
              // 提取追蹤頭
              traceID := r.Header.Get("X-Trace-ID")
              if traceID == "" {
                  traceID = uuid.NewString() // 無追蹤頭時生成隨機 ID
              }
              // 日志中添加 traceID 字段
              l = l.With("traceID", traceID)
              defer func() {
                  if err := recover(); err != nil {
                      // 異常日志關聯 traceID
                      l.Error("panic while serving request", "client", r.RemoteAddr, "url", r.URL, "err", err, "stack", buf)
                  }
              }()
              // 請求處理日志關聯 traceID
              l.Debug("serving request", "method", r.Method, "path", r.URL.Path, "traceID", traceID)
              h.ServeHTTP(w, r)
          })
      }
      posted @ 2025-10-24 14:42  左揚  閱讀(12)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 亚洲综合网一区中文字幕| 在线a亚洲v天堂网2018| 久久久久99精品成人片牛牛影视| 免费超爽大片黄| 精品无码久久久久久久动漫| 米奇亚洲国产精品思久久| 一边吃奶一边做动态图| 精品一区二区成人码动漫| 亚洲国产在一区二区三区| 999精品全免费观看视频| 一区二区三区午夜无码视频| 正定县| 欧美大bbbb流白水| 中文字幕在线亚洲日韩6页| 99福利一区二区视频| 久久久无码精品亚洲日韩蜜臀浪潮 | 青青草国产自产一区二区| 丁香婷婷综合激情五月色| 伊人av超碰伊人久久久| 亚洲五月天一区二区三区| 欧美肥妇毛多水多bbxx| 国产午夜伦伦午夜伦无码| 人人澡人摸人人添| 天峨县| 亚洲线精品一区二区三八戒 | 成人国产乱对白在线观看| 久久亚洲国产精品久久| 国产成人亚洲老熟女精品| 亚洲中文字幕无码av永久| 五月婷婷久久中文字幕| 亚洲日韩性欧美中文字幕| 唐人社导航福利精品| 蜜芽亚洲AV无码精品国产午夜 | 亚洲av无码牛牛影视在线二区| 国产精品综合av一区二区| 国产成人无码性教育视频| 国产成人高清精品免费软件| 美女扒开尿口让男人桶| 亚洲熟妇少妇任你躁在线观看无码| 国产日韩乱码精品一区二区| 国产精品十八禁一区二区|