gitee.com/ks-custle/core-gm@v0.0.0-20230922171213-b83bdd97b62c/grpc/health/server.go (about) 1 /* 2 * 3 * Copyright 2017 gRPC authors. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 * 17 */ 18 19 // Package health provides a service that exposes server's health and it must be 20 // imported to enable support for client-side health checks. 21 package health 22 23 import ( 24 "context" 25 "sync" 26 27 "gitee.com/ks-custle/core-gm/grpc/codes" 28 healthgrpc "gitee.com/ks-custle/core-gm/grpc/health/grpc_health_v1" 29 healthpb "gitee.com/ks-custle/core-gm/grpc/health/grpc_health_v1" 30 "gitee.com/ks-custle/core-gm/grpc/status" 31 ) 32 33 // Server implements `service Health`. 34 type Server struct { 35 healthgrpc.UnimplementedHealthServer 36 mu sync.RWMutex 37 // If shutdown is true, it's expected all serving status is NOT_SERVING, and 38 // will stay in NOT_SERVING. 39 shutdown bool 40 // statusMap stores the serving status of the services this Server monitors. 41 statusMap map[string]healthpb.HealthCheckResponse_ServingStatus 42 updates map[string]map[healthgrpc.Health_WatchServer]chan healthpb.HealthCheckResponse_ServingStatus 43 } 44 45 // NewServer returns a new Server. 46 func NewServer() *Server { 47 return &Server{ 48 statusMap: map[string]healthpb.HealthCheckResponse_ServingStatus{"": healthpb.HealthCheckResponse_SERVING}, 49 updates: make(map[string]map[healthgrpc.Health_WatchServer]chan healthpb.HealthCheckResponse_ServingStatus), 50 } 51 } 52 53 // Check implements `service Health`. 54 // 55 //goland:noinspection GoUnusedParameter 56 func (s *Server) Check(ctx context.Context, in *healthpb.HealthCheckRequest) (*healthpb.HealthCheckResponse, error) { 57 s.mu.RLock() 58 defer s.mu.RUnlock() 59 if servingStatus, ok := s.statusMap[in.Service]; ok { 60 return &healthpb.HealthCheckResponse{ 61 Status: servingStatus, 62 }, nil 63 } 64 return nil, status.Error(codes.NotFound, "unknown service") 65 } 66 67 // Watch implements `service Health`. 68 func (s *Server) Watch(in *healthpb.HealthCheckRequest, stream healthgrpc.Health_WatchServer) error { 69 service := in.Service 70 // update channel is used for getting service status updates. 71 update := make(chan healthpb.HealthCheckResponse_ServingStatus, 1) 72 s.mu.Lock() 73 // Puts the initial status to the channel. 74 if servingStatus, ok := s.statusMap[service]; ok { 75 update <- servingStatus 76 } else { 77 update <- healthpb.HealthCheckResponse_SERVICE_UNKNOWN 78 } 79 80 // Registers the update channel to the correct place in the updates map. 81 if _, ok := s.updates[service]; !ok { 82 s.updates[service] = make(map[healthgrpc.Health_WatchServer]chan healthpb.HealthCheckResponse_ServingStatus) 83 } 84 s.updates[service][stream] = update 85 defer func() { 86 s.mu.Lock() 87 delete(s.updates[service], stream) 88 s.mu.Unlock() 89 }() 90 s.mu.Unlock() 91 92 var lastSentStatus healthpb.HealthCheckResponse_ServingStatus = -1 93 for { 94 select { 95 // Status updated. Sends the up-to-date status to the client. 96 case servingStatus := <-update: 97 if lastSentStatus == servingStatus { 98 continue 99 } 100 lastSentStatus = servingStatus 101 err := stream.Send(&healthpb.HealthCheckResponse{Status: servingStatus}) 102 if err != nil { 103 return status.Error(codes.Canceled, "Stream has ended.") 104 } 105 // Context done. Removes the update channel from the updates map. 106 case <-stream.Context().Done(): 107 return status.Error(codes.Canceled, "Stream has ended.") 108 } 109 } 110 } 111 112 // SetServingStatus is called when need to reset the serving status of a service 113 // or insert a new service entry into the statusMap. 114 func (s *Server) SetServingStatus(service string, servingStatus healthpb.HealthCheckResponse_ServingStatus) { 115 s.mu.Lock() 116 defer s.mu.Unlock() 117 if s.shutdown { 118 logger.Infof("health: status changing for %s to %v is ignored because health service is shutdown", service, servingStatus) 119 return 120 } 121 122 s.setServingStatusLocked(service, servingStatus) 123 } 124 125 func (s *Server) setServingStatusLocked(service string, servingStatus healthpb.HealthCheckResponse_ServingStatus) { 126 s.statusMap[service] = servingStatus 127 for _, update := range s.updates[service] { 128 // Clears previous updates, that are not sent to the client, from the channel. 129 // This can happen if the client is not reading and the server gets flow control limited. 130 select { 131 case <-update: 132 default: 133 } 134 // Puts the most recent update to the channel. 135 update <- servingStatus 136 } 137 } 138 139 // Shutdown sets all serving status to NOT_SERVING, and configures the server to 140 // ignore all future status changes. 141 // 142 // This changes serving status for all services. To set status for a particular 143 // services, call SetServingStatus(). 144 func (s *Server) Shutdown() { 145 s.mu.Lock() 146 defer s.mu.Unlock() 147 s.shutdown = true 148 for service := range s.statusMap { 149 s.setServingStatusLocked(service, healthpb.HealthCheckResponse_NOT_SERVING) 150 } 151 } 152 153 // Resume sets all serving status to SERVING, and configures the server to 154 // accept all future status changes. 155 // 156 // This changes serving status for all services. To set status for a particular 157 // services, call SetServingStatus(). 158 func (s *Server) Resume() { 159 s.mu.Lock() 160 defer s.mu.Unlock() 161 s.shutdown = false 162 for service := range s.statusMap { 163 s.setServingStatusLocked(service, healthpb.HealthCheckResponse_SERVING) 164 } 165 }