最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Golang健康檢查接口的實(shí)現(xiàn)

 更新時(shí)間:2026年04月21日 09:36:58   作者:穿越時(shí)空  
本文主要介紹了Golang健康檢查接口的實(shí)現(xiàn),包括存活探針和就緒探針的區(qū)別,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧

Go 語(yǔ)言健康檢查接口的設(shè)計(jì)需要區(qū)分 存活探針(Liveness)就緒探針(Readiness),兩者在 Kubernetes 中的行為截然不同。以下是完整的設(shè)計(jì)指南和代碼實(shí)現(xiàn)。

1. 三種探針的區(qū)別

探針類型職責(zé)失敗后果適用場(chǎng)景
Liveness(存活)進(jìn)程是否活著重啟容器檢測(cè)死鎖、內(nèi)存泄漏導(dǎo)致的僵死
Readiness(就緒)是否準(zhǔn)備好接收流量從負(fù)載均衡移除初始化未完成、依賴服務(wù)不可用
Startup(啟動(dòng))慢啟動(dòng)應(yīng)用是否已啟動(dòng)禁用其他探針啟動(dòng)耗時(shí)長(zhǎng)的應(yīng)用(JVM/加載大量數(shù)據(jù))

2. Go 健康檢查接口實(shí)現(xiàn)

基礎(chǔ)版本(最小可用)

package main
import (
	"encoding/json"
	"log"
	"net/http"
	"sync/atomic"
	"time"
)
var ready atomic.Bool
func main() {
	go initialize()
	http.HandleFunc("/healthz", livenessHandler)   // 存活探針
	http.HandleFunc("/readyz", readinessHandler)   // 就緒探針
	http.HandleFunc("/startupz", startupHandler)   // 啟動(dòng)探針(可選)
	log.Println("Server starting on :8080")
	log.Fatal(http.ListenAndServe(":8080", nil))
}
func initialize() {
	log.Println("Initializing...")
	time.Sleep(5 * time.Second) // 模擬啟動(dòng)耗時(shí)
	ready.Store(true)
	log.Println("Ready")
}
// 存活探針:只檢查進(jìn)程是否活著,不要檢查外部依賴
func livenessHandler(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusOK)
	json.NewEncoder(w).Encode(map[string]string{"status": "alive"})
}
// 就緒探針:檢查應(yīng)用是否準(zhǔn)備好接收流量
func readinessHandler(w http.ResponseWriter, r *http.Request) {
	if !ready.Load() {
		w.WriteHeader(http.StatusServiceUnavailable)
		json.NewEncoder(w).Encode(map[string]string{"status": "not ready"})
		return
	}
	w.WriteHeader(http.StatusOK)
	json.NewEncoder(w).Encode(map[string]string{"status": "ready"})
}
// 啟動(dòng)探針:用于慢啟動(dòng)應(yīng)用
func startupHandler(w http.ResponseWriter, r *http.Request) {
	if !ready.Load() {
		w.WriteHeader(http.StatusServiceUnavailable)
		return
	}
	w.WriteHeader(http.StatusOK)
}

生產(chǎn)級(jí)版本(帶依賴檢查)

package health
import (
	"context"
	"database/sql"
	"encoding/json"
	"errors"
	"net/http"
	"sync"
	"time"
)
// Status 健康狀態(tài)
type Status string
const (
	StatusHealthy   Status = "healthy"
	StatusUnhealthy Status = "unhealthy"
	StatusDegraded  Status = "degraded"
)
// CheckResult 單個(gè)檢查項(xiàng)結(jié)果
type CheckResult struct {
	Name     string        `json:"name"`
	Status   Status        `json:"status"`
	Message  string        `json:"message,omitempty"`
	Duration time.Duration `json:"duration_ms,omitempty"`
}
// HealthResponse 整體健康響應(yīng)
type HealthResponse struct {
	Status    Status        `json:"status"`
	Timestamp time.Time     `json:"timestamp"`
	Checks    []CheckResult `json:"checks,omitempty"`
}
// Checker 依賴檢查器接口
type Checker interface {
	Name() string
	Check(ctx context.Context) CheckResult
}
// Service 健康檢查服務(wù)
type Service struct {
	checkers []Checker
	timeout  time.Duration
	mu       sync.RWMutex
	ready    bool
}
func NewService(timeout time.Duration) *Service {
	return &Service{timeout: timeout}
}
func (s *Service) Register(checkers ...Checker) {
	s.checkers = append(s.checkers, checkers...)
}
func (s *Service) SetReady(ready bool) {
	s.mu.Lock()
	defer s.mu.Unlock()
	s.ready = ready
}
func (s *Service) IsReady() bool {
	s.mu.RLock()
	defer s.mu.RUnlock()
	return s.ready
}
// LivenessHandler 存活探針 - 只檢查進(jìn)程是否活著
func (s *Service) LivenessHandler(w http.ResponseWriter, r *http.Request) {
	// 存活探針必須保持簡(jiǎn)單,不要檢查外部依賴
	// 否則數(shù)據(jù)庫(kù)故障會(huì)導(dǎo)致所有 Pod 被重啟,引發(fā)級(jí)聯(lián)故障 [^15^]
	response := HealthResponse{
		Status:    StatusHealthy,
		Timestamp: time.Now(),
	}
	w.WriteHeader(http.StatusOK)
	json.NewEncoder(w).Encode(response)
}
// ReadinessHandler 就緒探針 - 檢查依賴是否就緒
func (s *Service) ReadinessHandler(w http.ResponseWriter, r *http.Request) {
	if !s.IsReady() {
		w.WriteHeader(http.StatusServiceUnavailable)
		json.NewEncoder(w).Encode(HealthResponse{
			Status:    StatusUnhealthy,
			Timestamp: time.Now(),
			Message:   "service not initialized",
		})
		return
	}
	ctx, cancel := context.WithTimeout(r.Context(), s.timeout)
	defer cancel()
	results := make([]CheckResult, 0, len(s.checkers))
	overallStatus := StatusHealthy
	for _, checker := range s.checkers {
		start := time.Now()
		result := checker.Check(ctx)
		result.Duration = time.Since(start)
		results = append(results, result)
		if result.Status == StatusUnhealthy {
			overallStatus = StatusUnhealthy
		} else if result.Status == StatusDegraded && overallStatus == StatusHealthy {
			overallStatus = StatusDegraded
		}
	}
	response := HealthResponse{
		Status:    overallStatus,
		Timestamp: time.Now(),
		Checks:    results,
	}
	if overallStatus == StatusUnhealthy {
		w.WriteHeader(http.StatusServiceUnavailable)
	} else {
		w.WriteHeader(http.StatusOK)
	}
	json.NewEncoder(w).Encode(response)
}
// ========== 具體檢查器實(shí)現(xiàn) ==========
// DatabaseChecker 數(shù)據(jù)庫(kù)連接檢查
type DatabaseChecker struct {
	DB *sql.DB
}
func (d *DatabaseChecker) Name() string { return "database" }
func (d *DatabaseChecker) Check(ctx context.Context) CheckResult {
	// 使用 PingContext 而非復(fù)雜查詢,避免影響性能 [^15^]
	err := d.DB.PingContext(ctx)
	if err != nil {
		return CheckResult{
			Name:    d.Name(),
			Status:  StatusUnhealthy,
			Message: err.Error(),
		}
	}
	return CheckResult{
		Name:   d.Name(),
		Status: StatusHealthy,
	}
}
// RedisChecker Redis 連接檢查
type RedisChecker struct {
	// redis.Client
}
func (r *RedisChecker) Name() string { return "redis" }
func (r *RedisChecker) Check(ctx context.Context) CheckResult {
	// 實(shí)現(xiàn)類似 DatabaseChecker
	return CheckResult{Name: r.Name(), Status: StatusHealthy}
}
// ExternalAPIChecker 外部 API 檢查
type ExternalAPIChecker struct {
	URL    string
	Client *http.Client
}
func (e *ExternalAPIChecker) Name() string { return "external-api" }
func (e *ExternalAPIChecker) Check(ctx context.Context) CheckResult {
	req, _ := http.NewRequestWithContext(ctx, "GET", e.URL, nil)
	resp, err := e.Client.Do(req)
	if err != nil {
		return CheckResult{
			Name:    e.Name(),
			Status:  StatusDegraded, // 外部服務(wù)不可用,標(biāo)記為降級(jí)而非不可用
			Message: err.Error(),
		}
	}
	defer resp.Body.Close()
	if resp.StatusCode >= 200 && resp.StatusCode < 300 {
		return CheckResult{Name: e.Name(), Status: StatusHealthy}
	}
	return CheckResult{
		Name:    e.Name(),
		Status:  StatusDegraded,
		Message: "status: " + resp.Status,
	}
}

3. 優(yōu)雅關(guān)閉集成

package main
import (
	"context"
	"log"
	"net/http"
	"os"
	"os/signal"
	"syscall"
	"time"
)
func main() {
	healthSvc := health.NewService(3 * time.Second)
	healthSvc.Register(
		&health.DatabaseChecker{DB: db},
		&health.RedisChecker{},
	)
	mux := http.NewServeMux()
	mux.HandleFunc("/healthz", healthSvc.LivenessHandler)
	mux.HandleFunc("/readyz", healthSvc.ReadinessHandler)
	server := &http.Server{Addr: ":8080", Handler: mux}
	// 啟動(dòng)服務(wù)
	go func() {
		if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
			log.Fatal(err)
		}
	}()
	// 等待信號(hào)
	sigChan := make(chan os.Signal, 1)
	signal.Notify(sigChan, syscall.SIGTERM, syscall.SIGINT)
	<-sigChan
	// 優(yōu)雅關(guān)閉:先標(biāo)記為未就緒,停止接收新流量
	log.Println("Shutting down gracefully...")
	healthSvc.SetReady(false)
	// 等待負(fù)載均衡器更新(匹配 K8s readiness probe 周期)
	time.Sleep(10 * time.Second)
	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
	defer cancel()
	if err := server.Shutdown(ctx); err != nil {
		log.Printf("Server forced to shutdown: %v", err)
	}
	log.Println("Server exited")
}

4. Kubernetes 配置

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-go-app
spec:
  replicas: 3
  template:
    spec:
      containers:
        - name: app
          image: my-go-app:latest
          ports:
            - containerPort: 8080
          # 存活探針:只檢查進(jìn)程是否活著
          livenessProbe:
            httpGet:
              path: /healthz
              port: 8080
            initialDelaySeconds: 10   # 啟動(dòng)后等待時(shí)間
            periodSeconds: 10          # 檢查頻率
            timeoutSeconds: 3          # 超時(shí)時(shí)間(必須 < 代碼中的 timeout)
            failureThreshold: 3        # 連續(xù)失敗次數(shù)才重啟 [^12^][^15^]
          # 就緒探針:檢查依賴是否就緒
          readinessProbe:
            httpGet:
              path: /readyz
              port: 8080
            initialDelaySeconds: 5
            periodSeconds: 5           # 檢查頻率更高,快速切換流量
            timeoutSeconds: 3
            failureThreshold: 3
          # 啟動(dòng)探針:保護(hù)慢啟動(dòng)應(yīng)用(可選)
          startupProbe:
            httpGet:
              path: /readyz
              port: 8080
            failureThreshold: 30       # 最多允許 30 次失敗
            periodSeconds: 10          # 總啟動(dòng)時(shí)間 = 30 * 10 = 300s
          lifecycle:
            preStop:
              exec:
                command: ["/bin/sh", "-c", "sleep 10"]  # 給優(yōu)雅關(guān)閉留時(shí)間

5. 關(guān)鍵最佳實(shí)踐 

? 應(yīng)該做的

  • 存活探針保持簡(jiǎn)單:只檢查進(jìn)程是否響應(yīng),不要檢查數(shù)據(jù)庫(kù)/Redis,避免級(jí)聯(lián)重啟
  • 就緒探針檢查依賴:數(shù)據(jù)庫(kù)、緩存、配置是否加載完成
  • 超時(shí)設(shè)置合理:代碼中的 timeout < K8s timeoutSeconds,通常 2-3 秒
  • 使用原子操作atomic.Boolsync.RWMutex 管理就緒狀態(tài)
  • 記錄狀態(tài)變更:健康狀態(tài)變化時(shí)打印日志,便于排查

? 不應(yīng)該做的

  • 不要在存活探針里做 DB Ping:數(shù)據(jù)庫(kù)故障會(huì)導(dǎo)致所有 Pod 無(wú)限重啟
  • 不要做全表掃描或復(fù)雜查詢:健康檢查頻率高,避免影響性能
  • 不要返回過(guò)多信息:探針失敗時(shí) K8s 只關(guān)心狀態(tài)碼,詳細(xì)日志用于調(diào)試即可
  • 不要把外部服務(wù)失敗標(biāo)記為 Unhealthy:建議用 Degraded,避免流量全部切走

6. gRPC 健康檢查

如果服務(wù)使用 gRPC,需要實(shí)現(xiàn)標(biāo)準(zhǔn)健康檢查協(xié)議:

import (
	"google.golang.org/grpc/health/grpc_health_v1"
)
type HealthService struct{}
func (h *HealthService) Check(ctx context.Context, req *grpc_health_v1.HealthCheckRequest) (*grpc_health_v1.HealthCheckResponse, error) {
	return &grpc_health_v1.HealthCheckResponse{
		Status: grpc_health_v1.HealthCheckResponse_SERVING,
	}, nil
}
func (h *HealthService) Watch(req *grpc_health_v1.HealthCheckRequest, stream grpc_health_v1.Health_WatchServer) error {
	return nil
}
// 注冊(cè)
grpc_health_v1.RegisterHealthServer(grpcServer, &HealthService{})

Kubernetes v1.25+ 原生支持 gRPC 探針:

livenessProbe:
  grpc:
    port: 50051
    service: "my-service"

到此這篇關(guān)于Golang健康檢查接口的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)Golang健康檢查接口內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 一文帶你掌握GoLang中的指針

    一文帶你掌握GoLang中的指針

    針是?Go?編程語(yǔ)言的重要組成部分,它們提供了一種直接引用和操作內(nèi)存中數(shù)據(jù)的方法,在這篇文章中,我們將探討一些技巧和技巧,以掌握?GoLang?中的指針并編寫高效的代碼,需要的可以參考一下
    2023-05-05
  • Golang中Gin框架的使用入門教程

    Golang中Gin框架的使用入門教程

    這篇文章主要為大家詳細(xì)介紹了Golang中Gin框架的使用教程,文中通過(guò)簡(jiǎn)單的示例為大家講解了Gin框架的安裝與使用,感興趣的小伙伴開(kāi)業(yè)跟隨小編一起學(xué)習(xí)一下
    2022-10-10
  • golang?http請(qǐng)求未釋放造成的錯(cuò)誤問(wèn)題

    golang?http請(qǐng)求未釋放造成的錯(cuò)誤問(wèn)題

    這篇文章主要介紹了golang?http請(qǐng)求未釋放造成的錯(cuò)誤問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-01-01
  • go gin中間件關(guān)于 c.next()、c.abort()和return的使用小結(jié)

    go gin中間件關(guān)于 c.next()、c.abort()和return的使用小結(jié)

    中間件的執(zhí)行順序是按照注冊(cè)順序執(zhí)行的,中間件可以通過(guò) c.abort() + retrurn 來(lái)中止當(dāng)前中間件,后續(xù)中間件和處理器的處理流程,?這篇文章給大家介紹go gin中間件關(guān)于 c.next()、c.abort()和return的使用小結(jié),感興趣的朋友跟隨小編一起看看吧
    2024-03-03
  • go語(yǔ)言K8S?的?informer機(jī)制淺析

    go語(yǔ)言K8S?的?informer機(jī)制淺析

    這篇文章為大家主要介紹了go語(yǔ)言K8S?的?informer機(jī)制淺析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-10-10
  • Go中的格式化字符串fmt.Sprintf()和fmt.Printf()使用示例

    Go中的格式化字符串fmt.Sprintf()和fmt.Printf()使用示例

    這篇文章主要為大家介紹了Go中的格式化字符串fmt.Sprintf()和fmt.Printf()使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-06-06
  • golang踩坑實(shí)戰(zhàn)之channel的正確使用方式

    golang踩坑實(shí)戰(zhàn)之channel的正確使用方式

    Golang?channel是Go語(yǔ)言中一個(gè)非常重要的特性,除了用來(lái)處理并發(fā)編程的任務(wù)中,它還可以用來(lái)進(jìn)行消息傳遞和事件通知,這篇文章主要給大家介紹了關(guān)于golang踩坑實(shí)戰(zhàn)之channel的正確使用方式,需要的朋友可以參考下
    2023-06-06
  • Go?Redis客戶端使用的兩種對(duì)比

    Go?Redis客戶端使用的兩種對(duì)比

    這篇文章主要為大家介紹了Go?Redis客戶端使用對(duì)比詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-07-07
  • Golang連接Redis數(shù)據(jù)庫(kù)的方法

    Golang連接Redis數(shù)據(jù)庫(kù)的方法

    這篇文章主要介紹了Golang連接Redis數(shù)據(jù)庫(kù)的方法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-12-12
  • 使用Go實(shí)現(xiàn)一個(gè)百行聊天服務(wù)器的示例代碼

    使用Go實(shí)現(xiàn)一個(gè)百行聊天服務(wù)器的示例代碼

    前段時(shí)間, redis作者整了個(gè)c語(yǔ)言版本的聊天服務(wù)器,代碼量攏共不過(guò)百行,于是, 心血來(lái)潮下, 我也整了個(gè)Go語(yǔ)言版本, 簡(jiǎn)單來(lái)說(shuō)就是實(shí)現(xiàn)了一個(gè)聊天室的功能,文中通過(guò)代碼示例給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2023-12-12

最新評(píng)論

日土县| 福建省| 伽师县| 河间市| 衡东县| 西畴县| 威远县| 周至县| 庄河市| 商丘市| 娄底市| 汕尾市| 镇康县| 新闻| 龙游县| 合肥市| 长岭县| 秦皇岛市| 江都市| 灵璧县| 梁山县| 东明县| 新邵县| 昭通市| 青海省| 鹰潭市| 时尚| 旬阳县| 洪雅县| 庐江县| 沭阳县| 乐东| 弥勒县| 武义县| 凤山市| 平南县| 乌兰浩特市| 梓潼县| 安吉县| 金昌市| 松原市|