github.com/polarismesh/polaris@v1.17.8/service/healthcheck/cache.go (about) 1 /** 2 * Tencent is pleased to support the open source community by making Polaris available. 3 * 4 * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. 5 * 6 * Licensed under the BSD 3-Clause License (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * https://opensource.org/licenses/BSD-3-Clause 11 * 12 * Unless required by applicable law or agreed to in writing, software distributed 13 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 14 * CONDITIONS OF ANY KIND, either express or implied. See the License for the 15 * specific language governing permissions and limitations under the License. 16 */ 17 18 package healthcheck 19 20 import ( 21 "runtime" 22 23 apiservice "github.com/polarismesh/specification/source/go/api/v1/service_manage" 24 25 "github.com/polarismesh/polaris/common/hash" 26 commonhash "github.com/polarismesh/polaris/common/hash" 27 "github.com/polarismesh/polaris/common/model" 28 "github.com/polarismesh/polaris/common/utils" 29 "github.com/polarismesh/polaris/plugin" 30 ) 31 32 var DefaultShardSize uint32 33 34 func init() { 35 DefaultShardSize = uint32(runtime.GOMAXPROCS(0) * 16) 36 // Different machines can adjust this parameter of 16.In more cases, 16 is suitable , 37 // can test it in shardmap_test.go 38 } 39 40 // CacheProvider provider health check objects for service cache 41 type CacheProvider struct { 42 svr *Server 43 selfService string 44 selfServiceInstances *utils.SegmentMap[string, ItemWithChecker] 45 healthCheckInstances *utils.SegmentMap[string, ItemWithChecker] 46 healthCheckClients *utils.SegmentMap[string, ItemWithChecker] 47 } 48 49 // CacheEvent provides the event for cache changes 50 type CacheEvent struct { 51 healthCheckInstancesChanged bool 52 selfServiceInstancesChanged bool 53 healthCheckClientChanged bool 54 } 55 56 func newCacheProvider(selfService string, svr *Server) *CacheProvider { 57 return &CacheProvider{ 58 svr: svr, 59 selfService: selfService, 60 selfServiceInstances: utils.NewSegmentMap[string, ItemWithChecker](1, hash.Fnv32), 61 healthCheckInstances: utils.NewSegmentMap[string, ItemWithChecker](int(DefaultShardSize), hash.Fnv32), 62 healthCheckClients: utils.NewSegmentMap[string, ItemWithChecker](int(DefaultShardSize), hash.Fnv32), 63 } 64 } 65 66 func (c *CacheProvider) isSelfServiceInstance(instance *apiservice.Instance) bool { 67 metadata := instance.GetMetadata() 68 if svcName, ok := metadata[model.MetaKeyPolarisService]; ok { 69 return svcName == c.selfService 70 } 71 return false 72 } 73 74 // OnCreated callback when cache value created 75 func (c *CacheProvider) OnCreated(value interface{}) { 76 switch actual := value.(type) { 77 case *model.Instance: 78 instProto := actual.Proto 79 if c.isSelfServiceInstance(instProto) { 80 storeServiceInstance(newInstanceWithChecker(actual, nil), c.selfServiceInstances) 81 c.sendEvent(CacheEvent{selfServiceInstancesChanged: true}) 82 return 83 } 84 hcEnable, checker := c.isHealthCheckEnable(instProto) 85 if !hcEnable { 86 return 87 } 88 storeServiceInstance(newInstanceWithChecker(actual, checker), c.healthCheckInstances) 89 c.sendEvent(CacheEvent{healthCheckInstancesChanged: true}) 90 case *model.Client: 91 checker, ok := c.getHealthChecker(apiservice.HealthCheck_HEARTBEAT) 92 if !ok { 93 return 94 } 95 storeClient(newClientWithChecker(actual, checker), c.healthCheckClients) 96 c.sendEvent(CacheEvent{healthCheckClientChanged: true}) 97 } 98 } 99 100 func (c *CacheProvider) getHealthChecker(hcType apiservice.HealthCheck_HealthCheckType) (plugin.HealthChecker, bool) { 101 checker, ok := c.svr.checkers[int32(hcType)] 102 return checker, ok 103 } 104 105 func (c *CacheProvider) isHealthCheckEnable(instance *apiservice.Instance) (bool, plugin.HealthChecker) { 106 if !instance.GetEnableHealthCheck().GetValue() || instance.GetHealthCheck() == nil { 107 return false, nil 108 } 109 checker, ok := c.getHealthChecker(instance.GetHealthCheck().GetType()) 110 if !ok { 111 return false, nil 112 } 113 return true, checker 114 } 115 116 // OnUpdated callback when cache value updated 117 func (c *CacheProvider) OnUpdated(value interface{}) { 118 switch actual := value.(type) { 119 case *model.Instance: 120 instProto := actual.Proto 121 if c.isSelfServiceInstance(instProto) { 122 if compareAndStoreServiceInstance(newInstanceWithChecker(actual, nil), c.selfServiceInstances) { 123 c.sendEvent(CacheEvent{selfServiceInstancesChanged: true}) 124 } 125 return 126 } 127 // check exists 128 instanceId := actual.ID() 129 value, exists := c.healthCheckInstances.Get(instanceId) 130 hcEnable, checker := c.isHealthCheckEnable(instProto) 131 if !hcEnable { 132 if !exists { 133 // instance is unhealthy, not exist, just return. 134 return 135 } 136 log.Infof("[Health Check][Cache]delete health check disabled instance is %s:%d, id is %s", 137 actual.Host(), actual.Port(), instanceId) 138 // instance is unhealthy, but exist, delete it. 139 ok := c.healthCheckInstances.Del(instanceId) 140 if ok { 141 c.sendEvent(CacheEvent{healthCheckInstancesChanged: true}) 142 } 143 return 144 } 145 var noChanged bool 146 if exists { 147 // instance is healthy, exists, consistent healthCheckInstance.Revision(), no need to change。 148 healthCheckInstance := value.GetInstance() 149 noChanged = healthCheckInstance.Revision() == actual.Revision() 150 } 151 if !noChanged { 152 log.Infof("[Health Check][Cache]update service instance is %s:%d, id is %s", 153 actual.Host(), actual.Port(), instanceId) 154 // In the concurrent scenario, when the healthCheckInstance.Revision() of the same health instance is the same, 155 // if it arrives here at the same time, it will be saved multiple times 156 c.healthCheckInstances.Put(instanceId, newInstanceWithChecker(actual, checker)) 157 c.sendEvent(CacheEvent{healthCheckInstancesChanged: true}) 158 } 159 case *model.Client: 160 checker, ok := c.getHealthChecker(apiservice.HealthCheck_HEARTBEAT) 161 if !ok { 162 return 163 } 164 if compareAndStoreClient(newClientWithChecker(actual, checker), c.healthCheckClients) { 165 c.sendEvent(CacheEvent{healthCheckClientChanged: true}) 166 } 167 } 168 } 169 170 // OnDeleted callback when cache value deleted 171 func (c *CacheProvider) OnDeleted(value interface{}) { 172 switch actual := value.(type) { 173 case *model.Instance: 174 instProto := actual.Proto 175 if c.isSelfServiceInstance(instProto) { 176 deleteServiceInstance(instProto, c.selfServiceInstances) 177 c.sendEvent(CacheEvent{selfServiceInstancesChanged: true}) 178 return 179 } 180 if !instProto.GetEnableHealthCheck().GetValue() || instProto.GetHealthCheck() == nil { 181 return 182 } 183 deleteServiceInstance(instProto, c.healthCheckInstances) 184 c.sendEvent(CacheEvent{healthCheckInstancesChanged: true}) 185 case *model.Client: 186 deleteClient(actual.Proto(), c.healthCheckClients) 187 c.sendEvent(CacheEvent{healthCheckClientChanged: true}) 188 } 189 } 190 191 // OnBatchCreated callback when cache value created 192 func (c *CacheProvider) OnBatchCreated(value interface{}) { 193 194 } 195 196 // OnBatchUpdated callback when cache value updated 197 func (c *CacheProvider) OnBatchUpdated(value interface{}) { 198 199 } 200 201 // OnBatchDeleted callback when cache value deleted 202 func (c *CacheProvider) OnBatchDeleted(value interface{}) { 203 204 } 205 206 // RangeHealthCheckInstances range loop values 207 func (c *CacheProvider) RangeHealthCheckInstances(check func(itemChecker ItemWithChecker, ins *model.Instance)) { 208 c.healthCheckInstances.Range(func(instanceId string, value ItemWithChecker) { 209 check(value, value.GetInstance()) 210 }) 211 } 212 213 // RangeHealthCheckClients range loop values 214 func (c *CacheProvider) RangeHealthCheckClients(check func(itemChecker ItemWithChecker, client *model.Client)) { 215 c.healthCheckClients.Range(func(instanceId string, value ItemWithChecker) { 216 check(value, value.GetClient()) 217 }) 218 } 219 220 // RangeSelfServiceInstances range loop selfServiceInstances 221 func (c *CacheProvider) RangeSelfServiceInstances(check func(instance *apiservice.Instance)) { 222 c.selfServiceInstances.Range(func(instanceId string, value ItemWithChecker) { 223 check(value.GetInstance().Proto) 224 }) 225 } 226 227 // GetSelfServiceInstance get self service instance by id 228 func (c *CacheProvider) GetSelfServiceInstance(instanceId string) *model.Instance { 229 value, ok := c.selfServiceInstances.Get(instanceId) 230 if !ok { 231 return nil 232 } 233 234 ins := value.GetInstance() 235 if ins == nil { 236 return nil 237 } 238 return ins 239 } 240 241 // GetInstance get instance by id 242 func (c *CacheProvider) GetInstance(instanceId string) *model.Instance { 243 value, ok := c.healthCheckInstances.Get(instanceId) 244 if !ok { 245 return nil 246 } 247 248 ins := value.GetInstance() 249 if ins == nil { 250 return nil 251 } 252 return ins 253 } 254 255 // GetInstance get instance by id 256 func (c *CacheProvider) GetClient(clientId string) *model.Client { 257 value, ok := c.healthCheckClients.Get(clientId) 258 if !ok { 259 return nil 260 } 261 262 client := value.GetClient() 263 if client == nil { 264 return nil 265 } 266 return client 267 } 268 269 func (c *CacheProvider) sendEvent(event CacheEvent) { 270 c.svr.dispatcher.UpdateStatusByEvent(event) 271 } 272 273 func compareAndStoreServiceInstance(instanceWithChecker *InstanceWithChecker, 274 values *utils.SegmentMap[string, ItemWithChecker]) bool { 275 instanceId := instanceWithChecker.instance.ID() 276 value, isNew := values.PutIfAbsent(instanceId, instanceWithChecker) 277 if isNew { 278 log.Infof("[Health Check][Cache]create service instance is %s:%d, id is %s", 279 instanceWithChecker.instance.Host(), instanceWithChecker.instance.Port(), 280 instanceId) 281 return true 282 } 283 instanceValue := value.(*InstanceWithChecker) 284 lastInstance := instanceValue.instance 285 if lastInstance.Revision() == instanceWithChecker.instance.Revision() { 286 return false 287 } 288 log.Infof("[Health Check][Cache]update service instance is %s:%d, id is %s", 289 instanceWithChecker.instance.Host(), instanceWithChecker.instance.Port(), instanceId) 290 // In the concurrent scenario, when the key and version are the same, 291 // if they arrive here at the same time, they will be saved multiple times. 292 values.Put(instanceId, instanceWithChecker) 293 return true 294 } 295 296 func storeServiceInstance(instanceWithChecker *InstanceWithChecker, 297 values *utils.SegmentMap[string, ItemWithChecker]) bool { 298 log.Infof("[Health Check][Cache]create service instance is %s:%d, id is %s", 299 instanceWithChecker.instance.Host(), instanceWithChecker.instance.Port(), 300 instanceWithChecker.instance.ID()) 301 instanceId := instanceWithChecker.instance.ID() 302 values.Put(instanceId, instanceWithChecker) 303 return true 304 } 305 306 func deleteServiceInstance(instance *apiservice.Instance, values *utils.SegmentMap[string, ItemWithChecker]) bool { 307 instanceId := instance.GetId().GetValue() 308 ok := values.Del(instanceId) 309 if ok { 310 log.Infof("[Health Check][Cache]delete service instance is %s:%d, id is %s", 311 instance.GetHost().GetValue(), instance.GetPort().GetValue(), instanceId) 312 } 313 return true 314 } 315 316 func compareAndStoreClient(clientWithChecker *ClientWithChecker, 317 values *utils.SegmentMap[string, ItemWithChecker]) bool { 318 clientId := clientWithChecker.client.Proto().GetId().GetValue() 319 _, isNew := values.PutIfAbsent(clientId, clientWithChecker) 320 if isNew { 321 log.Infof("[Health Check][Cache]create client is %s, id is %s", 322 clientWithChecker.client.Proto().GetHost().GetValue(), clientId) 323 return true 324 } 325 return false 326 } 327 328 func storeClient(clientWithChecker *ClientWithChecker, 329 values *utils.SegmentMap[string, ItemWithChecker]) bool { 330 331 log.Infof("[Health Check][Cache]create client is %s, id is %s", 332 clientWithChecker.client.Proto().GetHost().GetValue(), clientWithChecker.client.Proto().GetId().GetValue()) 333 clientId := clientWithChecker.client.Proto().GetId().GetValue() 334 values.Put(clientId, clientWithChecker) 335 return true 336 } 337 338 func deleteClient(client *apiservice.Client, values *utils.SegmentMap[string, ItemWithChecker]) bool { 339 clientId := client.GetId().GetValue() 340 ok := values.Del(clientId) 341 if ok { 342 log.Infof("[Health Check][Cache]delete client is %s, id is %s", 343 client.GetHost().GetValue(), clientId) 344 } 345 return true 346 } 347 348 // ItemWithChecker item and checker combine 349 // GetInstance 与 GetClient 互斥 350 type ItemWithChecker interface { 351 // GetInstance 获取服务实例 352 GetInstance() *model.Instance 353 // GetClient 获取上报客户端信息 354 GetClient() *model.Client 355 // GetChecker 获取对应的 checker 对象 356 GetChecker() plugin.HealthChecker 357 // GetHashValue 获取 hashvalue 信息 358 GetHashValue() uint 359 } 360 361 // InstanceWithChecker instance and checker combine 362 type InstanceWithChecker struct { 363 instance *model.Instance 364 checker plugin.HealthChecker 365 hashValue uint 366 } 367 368 // GetInstance 获取服务实例 369 func (ic *InstanceWithChecker) GetInstance() *model.Instance { 370 return ic.instance 371 } 372 373 // GetClient 获取上报客户端信息 374 func (ic *InstanceWithChecker) GetClient() *model.Client { 375 return nil 376 } 377 378 // GetChecker 获取对应的 checker 对象 379 func (ic *InstanceWithChecker) GetChecker() plugin.HealthChecker { 380 return ic.checker 381 } 382 383 // GetHashValue 获取 hashvalue 信息 384 func (ic *InstanceWithChecker) GetHashValue() uint { 385 return ic.hashValue 386 } 387 388 func newInstanceWithChecker(instance *model.Instance, checker plugin.HealthChecker) *InstanceWithChecker { 389 return &InstanceWithChecker{ 390 instance: instance, 391 checker: checker, 392 hashValue: commonhash.HashString(instance.ID()), 393 } 394 } 395 396 // ClientWithChecker instance and checker combine 397 type ClientWithChecker struct { 398 client *model.Client 399 checker plugin.HealthChecker 400 hashValue uint 401 } 402 403 // GetInstance 获取服务实例 404 func (ic *ClientWithChecker) GetInstance() *model.Instance { 405 return nil 406 } 407 408 // GetClient 获取上报客户端信息 409 func (ic *ClientWithChecker) GetClient() *model.Client { 410 return ic.client 411 } 412 413 // GetChecker 获取对应的 checker 对象 414 func (ic *ClientWithChecker) GetChecker() plugin.HealthChecker { 415 return ic.checker 416 } 417 418 // GetHashValue 获取 hashvalue 信息 419 func (ic *ClientWithChecker) GetHashValue() uint { 420 return ic.hashValue 421 } 422 423 func newClientWithChecker(client *model.Client, checker plugin.HealthChecker) *ClientWithChecker { 424 return &ClientWithChecker{ 425 client: client, 426 checker: checker, 427 hashValue: commonhash.HashString(client.Proto().GetId().GetValue()), 428 } 429 }