package grpchealth import ( "context" "time" "google.golang.org/grpc/codes" healthgrpc "google.golang.org/grpc/health/grpc_health_v1" healthpb "google.golang.org/grpc/health/grpc_health_v1" "google.golang.org/grpc/status" ) const ( // StatusOK 表示单个健康检查项通过。 StatusOK = "ok" // StatusFail 表示单个健康检查项失败。 StatusFail = "fail" ) // Check 描述一项 readiness 子检查;gRPC health 只返回总状态,子项用于服务内聚合。 type Check struct { Name string `json:"name"` Status string `json:"status"` Message string `json:"message,omitempty"` } // Checker 把服务自己的 ready 语义适配到标准 gRPC health protocol。 type Checker interface { ServiceName() string Ready(ctx context.Context) []Check } // Server 实现 grpc.health.v1.Health,并把 SERVING 严格映射到 Checker.Ready。 type Server struct { healthgrpc.UnimplementedHealthServer checker Checker } // NewServer 创建标准 gRPC health server。 func NewServer(checker Checker) *Server { return &Server{checker: checker} } // Check 返回当前服务是否 ready;未知服务名按标准返回 NotFound。 func (s *Server) Check(ctx context.Context, req *healthpb.HealthCheckRequest) (*healthpb.HealthCheckResponse, error) { if !s.supports(req.GetService()) { return nil, status.Error(codes.NotFound, "unknown service") } return &healthpb.HealthCheckResponse{Status: s.status(ctx)}, nil } // Watch 定时推送 ready 状态变化,供支持 gRPC health watch 的服务发现系统消费。 func (s *Server) Watch(req *healthpb.HealthCheckRequest, stream healthgrpc.Health_WatchServer) error { if !s.supports(req.GetService()) { if err := stream.Send(&healthpb.HealthCheckResponse{Status: healthpb.HealthCheckResponse_SERVICE_UNKNOWN}); err != nil { return status.Error(codes.Canceled, "health watch stream ended") } <-stream.Context().Done() return status.Error(codes.Canceled, "health watch stream ended") } ticker := time.NewTicker(2 * time.Second) defer ticker.Stop() var last healthpb.HealthCheckResponse_ServingStatus = -1 for { current := s.status(stream.Context()) if current != last { if err := stream.Send(&healthpb.HealthCheckResponse{Status: current}); err != nil { return status.Error(codes.Canceled, "health watch stream ended") } last = current } select { case <-ticker.C: case <-stream.Context().Done(): return status.Error(codes.Canceled, "health watch stream ended") } } } // Healthy 聚合子检查,任何失败项都会让服务从 ready 池摘除。 func Healthy(checks []Check) bool { for _, check := range checks { if check.Status != StatusOK { return false } } return true } // OK 创建通过项。 func OK(name string) Check { return Check{Name: name, Status: StatusOK} } // Fail 创建失败项。 func Fail(name string, message string) Check { return Check{Name: name, Status: StatusFail, Message: message} } // Bool 根据布尔条件创建检查项。 func Bool(name string, ok bool, message string) Check { if ok { return OK(name) } return Fail(name, message) } func (s *Server) supports(service string) bool { return service == "" || service == s.checker.ServiceName() } func (s *Server) status(ctx context.Context) healthpb.HealthCheckResponse_ServingStatus { if Healthy(s.checker.Ready(ctx)) { return healthpb.HealthCheckResponse_SERVING } return healthpb.HealthCheckResponse_NOT_SERVING }