github.com/polarismesh/polaris@v1.17.8/service/healthcheck/check.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 "context" 22 "sync" 23 "time" 24 25 apimodel "github.com/polarismesh/specification/source/go/api/v1/model" 26 apiservice "github.com/polarismesh/specification/source/go/api/v1/service_manage" 27 "go.uber.org/zap" 28 29 "github.com/polarismesh/polaris/common/model" 30 "github.com/polarismesh/polaris/common/srand" 31 commonstore "github.com/polarismesh/polaris/common/store" 32 "github.com/polarismesh/polaris/common/timewheel" 33 "github.com/polarismesh/polaris/common/utils" 34 "github.com/polarismesh/polaris/plugin" 35 ) 36 37 const ( 38 expireTtlCount = 3 39 ) 40 41 // CheckScheduler schedule and run check actions 42 type CheckScheduler struct { 43 rwMutex *sync.RWMutex 44 scheduledInstances map[string]*itemValue 45 scheduledClients map[string]*clientItemValue 46 47 timeWheel *timewheel.TimeWheel 48 minCheckIntervalSec int64 49 maxCheckIntervalSec int64 50 clientCheckIntervalSec int64 51 clientCheckTtlSec int64 52 53 adoptInstancesChan chan AdoptEvent 54 ctx context.Context 55 } 56 57 // AdoptEvent is the event for adopt 58 type AdoptEvent struct { 59 InstanceId string 60 Add bool 61 Checker plugin.HealthChecker 62 } 63 64 type clientItemValue struct { 65 itemValue 66 lastCheckTimeSec int64 67 } 68 69 type itemValue struct { 70 mutex *sync.Mutex 71 id string 72 host string 73 port uint32 74 scheduled uint32 75 ttlDurationSec uint32 76 expireDurationSec uint32 77 checker plugin.HealthChecker 78 } 79 80 type InstanceEventHealthCheckHandler struct { 81 ctx context.Context 82 instanceEventChannel chan *model.InstanceEvent 83 } 84 85 // newLeaderChangeEventHandler 86 func newInstanceEventHealthCheckHandler(ctx context.Context, 87 eventChannel chan *model.InstanceEvent) *InstanceEventHealthCheckHandler { 88 return &InstanceEventHealthCheckHandler{ 89 ctx: ctx, 90 instanceEventChannel: eventChannel, 91 } 92 } 93 94 func (handler *InstanceEventHealthCheckHandler) PreProcess(ctx context.Context, value any) any { 95 return value 96 } 97 98 // OnEvent event trigger 99 func (handler *InstanceEventHealthCheckHandler) OnEvent(ctx context.Context, i interface{}) error { 100 e := i.(model.InstanceEvent) 101 select { 102 case handler.instanceEventChannel <- &e: 103 log.Debugf("[Health Check]get instance event, id is %s, type is %s", e.Id, e.EType) 104 default: 105 log.Errorf("[Health Check]instance event chan full, drop event, id is %s, type is %s", e.Id, e.EType) 106 } 107 return nil 108 } 109 110 func newCheckScheduler(ctx context.Context, slotNum int, minCheckInterval time.Duration, 111 maxCheckInterval time.Duration, clientCheckInterval time.Duration, clientCheckTtl time.Duration) *CheckScheduler { 112 scheduler := &CheckScheduler{ 113 rwMutex: &sync.RWMutex{}, 114 scheduledInstances: make(map[string]*itemValue), 115 scheduledClients: make(map[string]*clientItemValue), 116 timeWheel: timewheel.New(time.Second, slotNum, "health-interval-check"), 117 minCheckIntervalSec: int64(minCheckInterval.Seconds()), 118 maxCheckIntervalSec: int64(maxCheckInterval.Seconds()), 119 clientCheckIntervalSec: int64(clientCheckInterval.Seconds()), 120 clientCheckTtlSec: int64(clientCheckTtl.Seconds()), 121 adoptInstancesChan: make(chan AdoptEvent, 1024), 122 ctx: ctx, 123 } 124 return scheduler 125 } 126 127 func (c *CheckScheduler) run(ctx context.Context) { 128 go c.doCheckInstances(ctx) 129 go c.doCheckClient(ctx) 130 go c.doAdopt(ctx) 131 } 132 133 func (c *CheckScheduler) doCheckInstances(ctx context.Context) { 134 c.timeWheel.Start() 135 log.Infof("[Health Check][Check]timeWheel has been started") 136 137 <-ctx.Done() 138 c.timeWheel.Stop() 139 log.Infof("[Health Check][Check]timeWheel has been stopped") 140 } 141 142 const ( 143 batchAdoptInterval = 30 * time.Millisecond 144 batchAdoptCount = 30 145 ) 146 147 func (c *CheckScheduler) doAdopt(ctx context.Context) { 148 instancesToAdd := make(map[string]bool) 149 instancesToRemove := make(map[string]bool) 150 var checker plugin.HealthChecker 151 ticker := time.NewTicker(batchAdoptInterval) 152 defer func() { 153 ticker.Stop() 154 }() 155 for { 156 select { 157 case event := <-c.adoptInstancesChan: 158 instanceId := event.InstanceId 159 if event.Add { 160 instancesToAdd[instanceId] = true 161 delete(instancesToRemove, instanceId) 162 } else { 163 instancesToRemove[instanceId] = true 164 delete(instancesToAdd, instanceId) 165 } 166 checker = event.Checker 167 if len(instancesToAdd) == batchAdoptCount { 168 instancesToAdd = c.processAdoptEvents(instancesToAdd, true, checker) 169 } 170 if len(instancesToRemove) == batchAdoptCount { 171 instancesToRemove = c.processAdoptEvents(instancesToRemove, false, checker) 172 } 173 case <-ticker.C: 174 if len(instancesToAdd) > 0 { 175 instancesToAdd = c.processAdoptEvents(instancesToAdd, true, checker) 176 } 177 if len(instancesToRemove) > 0 { 178 instancesToRemove = c.processAdoptEvents(instancesToRemove, false, checker) 179 } 180 case <-ctx.Done(): 181 log.Infof("[Health Check][Check]adopting routine has been stopped") 182 return 183 } 184 } 185 } 186 187 func (c *CheckScheduler) processAdoptEvents( 188 instances map[string]bool, add bool, checker plugin.HealthChecker) map[string]bool { 189 instanceIds := make([]string, 0, len(instances)) 190 for id := range instances { 191 instanceIds = append(instanceIds, id) 192 } 193 log.Debug("[Health Check][Check] adopt event", zap.Any("instances", instanceIds), 194 zap.String("server", server.localHost), zap.Bool("add", add)) 195 return instances 196 } 197 198 func (c *CheckScheduler) addAdopting(instanceId string, checker plugin.HealthChecker) { 199 select { 200 case c.adoptInstancesChan <- AdoptEvent{ 201 InstanceId: instanceId, 202 Add: true, 203 Checker: checker}: 204 case <-c.ctx.Done(): 205 return 206 } 207 } 208 209 func (c *CheckScheduler) removeAdopting(instanceId string, checker plugin.HealthChecker) { 210 select { 211 case c.adoptInstancesChan <- AdoptEvent{ 212 InstanceId: instanceId, 213 Add: false, 214 Checker: checker}: 215 case <-c.ctx.Done(): 216 return 217 } 218 } 219 220 func (c *CheckScheduler) upsertInstanceChecker(instanceWithChecker *InstanceWithChecker) (bool, *itemValue) { 221 c.rwMutex.Lock() 222 defer c.rwMutex.Unlock() 223 instance := instanceWithChecker.instance 224 ttl := instance.HealthCheck().GetHeartbeat().GetTtl().GetValue() 225 var ( 226 instValue *itemValue 227 exist bool 228 ) 229 instValue, exist = c.scheduledInstances[instance.ID()] 230 if exist { 231 if ttl == instValue.ttlDurationSec { 232 return true, instValue 233 } 234 // force update check info 235 instValue.mutex.Lock() 236 oldTtl := instValue.ttlDurationSec 237 instValue.checker = instanceWithChecker.checker 238 instValue.expireDurationSec = getExpireDurationSec(instance.Proto) 239 instValue.ttlDurationSec = ttl 240 instValue.mutex.Unlock() 241 if log.DebugEnabled() { 242 log.Debug("[Health Check][Check] upsert instance checker", zap.String("id", instValue.id), 243 zap.Uint32("old-ttl", oldTtl), zap.Uint32("ttl", instValue.ttlDurationSec)) 244 } 245 } else { 246 instValue = &itemValue{ 247 mutex: &sync.Mutex{}, 248 host: instance.Host(), 249 port: instance.Port(), 250 id: instance.ID(), 251 expireDurationSec: getExpireDurationSec(instance.Proto), 252 checker: instanceWithChecker.checker, 253 ttlDurationSec: ttl, 254 } 255 } 256 c.scheduledInstances[instance.ID()] = instValue 257 return exist, instValue 258 } 259 260 func (c *CheckScheduler) putClientIfAbsent(clientWithChecker *ClientWithChecker) (bool, *clientItemValue) { 261 c.rwMutex.Lock() 262 defer c.rwMutex.Unlock() 263 client := clientWithChecker.client 264 var instValue *clientItemValue 265 var ok bool 266 clientId := client.Proto().GetId().GetValue() 267 if instValue, ok = c.scheduledClients[clientId]; ok { 268 return true, instValue 269 } 270 instValue = &clientItemValue{ 271 itemValue: itemValue{ 272 mutex: &sync.Mutex{}, 273 host: client.Proto().GetHost().GetValue(), 274 port: 0, 275 id: clientId, 276 expireDurationSec: uint32(expireTtlCount * c.clientCheckTtlSec), 277 checker: clientWithChecker.checker, 278 ttlDurationSec: uint32(c.clientCheckTtlSec), 279 }, 280 lastCheckTimeSec: 0, 281 } 282 c.scheduledClients[clientId] = instValue 283 return false, instValue 284 } 285 286 func (c *CheckScheduler) getInstanceValue(instanceId string) (*itemValue, bool) { 287 c.rwMutex.RLock() 288 defer c.rwMutex.RUnlock() 289 value, ok := c.scheduledInstances[instanceId] 290 return value, ok 291 } 292 293 func (c *CheckScheduler) getClientValue(clientId string) (*clientItemValue, bool) { 294 c.rwMutex.RLock() 295 defer c.rwMutex.RUnlock() 296 value, ok := c.scheduledClients[clientId] 297 return value, ok 298 } 299 300 // UpsertInstance insert or update instance to check 301 func (c *CheckScheduler) UpsertInstance(instanceWithChecker *InstanceWithChecker) { 302 firstadd, instValue := c.upsertInstanceChecker(instanceWithChecker) 303 if firstadd { 304 return 305 } 306 c.addAdopting(instValue.id, instValue.checker) 307 instance := instanceWithChecker.instance 308 log.Infof("[Health Check][Check]add check instance is %s, host is %s:%d", 309 instance.ID(), instance.Host(), instance.Port()) 310 c.addUnHealthyCallback(instValue) 311 } 312 313 // AddClient add client to check 314 func (c *CheckScheduler) AddClient(clientWithChecker *ClientWithChecker) { 315 exists, instValue := c.putClientIfAbsent(clientWithChecker) 316 if exists { 317 return 318 } 319 c.addAdopting(instValue.id, instValue.checker) 320 client := clientWithChecker.client 321 log.Infof("[Health Check][Check]add check client is %s, host is %s:%d", 322 client.Proto().GetId().GetValue(), client.Proto().GetHost(), 0) 323 } 324 325 func getExpireDurationSec(instance *apiservice.Instance) uint32 { 326 ttlValue := instance.GetHealthCheck().GetHeartbeat().GetTtl().GetValue() 327 return expireTtlCount * ttlValue 328 } 329 330 func getRandDelayMilli() uint32 { 331 delayMilli := srand.Intn(1000) 332 return uint32(delayMilli) 333 } 334 335 func (c *CheckScheduler) addHealthyCallback(instance *itemValue, lastHeartbeatTimeSec int64) { 336 delaySec := instance.expireDurationSec 337 var nextDelaySec int64 338 if lastHeartbeatTimeSec > 0 { 339 curTimeSec := currentTimeSec() 340 timePassed := curTimeSec - lastHeartbeatTimeSec 341 if timePassed > 0 { 342 nextDelaySec = int64(delaySec) - timePassed 343 } 344 } 345 if nextDelaySec > 0 && nextDelaySec < c.minCheckIntervalSec { 346 nextDelaySec = c.minCheckIntervalSec 347 } 348 if nextDelaySec > 0 { 349 delaySec = uint32(nextDelaySec) 350 } 351 host := instance.host 352 port := instance.port 353 instanceId := instance.id 354 delayMilli := delaySec*1000 + getRandDelayMilli() 355 log.Debugf("[Health Check][Check]add healthy instance callback, addr is %s:%d, id is %s, delay is %d(ms)", 356 host, port, instanceId, delayMilli) 357 c.timeWheel.AddTask(delayMilli, instanceId, c.checkCallbackInstance) 358 } 359 360 func (c *CheckScheduler) addUnHealthyCallback(instance *itemValue) { 361 delaySec := instance.expireDurationSec 362 if c.maxCheckIntervalSec > 0 && int64(delaySec) > c.maxCheckIntervalSec { 363 delaySec = uint32(c.maxCheckIntervalSec) 364 } 365 host := instance.host 366 port := instance.port 367 instanceId := instance.id 368 delayMilli := delaySec*1000 + getRandDelayMilli() 369 log.Debugf("[Health Check][Check]add unhealthy instance callback, addr is %s:%d, id is %s, delay is %d(ms)", 370 host, port, instanceId, delayMilli) 371 c.timeWheel.AddTask(delayMilli, instanceId, c.checkCallbackInstance) 372 } 373 374 func (c *CheckScheduler) checkCallbackClient(clientId string) *clientItemValue { 375 clientValue, ok := c.getClientValue(clientId) 376 if !ok { 377 log.Infof("[Health Check][Check]client %s has been removed from callback", clientId) 378 return nil 379 } 380 clientValue.mutex.Lock() 381 defer clientValue.mutex.Unlock() 382 var checkResp *plugin.CheckResponse 383 var err error 384 cachedClient := server.cacheProvider.GetClient(clientId) 385 if cachedClient == nil { 386 log.Infof("[Health Check][Check]client %s has been deleted", clientValue.id) 387 return clientValue 388 } 389 request := &plugin.CheckRequest{ 390 QueryRequest: plugin.QueryRequest{ 391 InstanceId: toClientId(clientValue.id), 392 Host: clientValue.host, 393 Port: clientValue.port, 394 Healthy: true, 395 }, 396 CurTimeSec: currentTimeSec, 397 ExpireDurationSec: clientValue.expireDurationSec, 398 } 399 checkResp, err = clientValue.checker.Check(request) 400 if err != nil { 401 log.Errorf("[Health Check][Check]fail to check client %s, id is %s, err is %v", 402 clientValue.host, clientValue.id, err) 403 return clientValue 404 } 405 if !checkResp.StayUnchanged { 406 if !checkResp.Healthy { 407 log.Infof( 408 "[Health Check][Check]client change from healthy to unhealthy, id is %s, address is %s", 409 clientValue.id, clientValue.host) 410 code := asyncDeleteClient(cachedClient.Proto()) 411 if code != apimodel.Code_ExecuteSuccess { 412 log.Errorf("[Health Check][Check]fail to update client, id is %s, address is %s, code is %d", 413 clientValue.id, clientValue.host, code) 414 } 415 } 416 } 417 return clientValue 418 } 419 420 func (c *CheckScheduler) checkCallbackInstance(value interface{}) { 421 instanceId := value.(string) 422 instanceValue, ok := c.getInstanceValue(instanceId) 423 if !ok { 424 log.Infof("[Health Check][Check]instance %s has been removed from callback", instanceId) 425 return 426 } 427 428 instanceValue.mutex.Lock() 429 defer instanceValue.mutex.Unlock() 430 431 var ( 432 checkResp *plugin.CheckResponse 433 err error 434 ) 435 defer func() { 436 if checkResp != nil && checkResp.Regular && checkResp.Healthy { 437 c.addHealthyCallback(instanceValue, checkResp.LastHeartbeatTimeSec) 438 } else { 439 c.addUnHealthyCallback(instanceValue) 440 } 441 }() 442 443 cachedInstance := server.cacheProvider.GetInstance(instanceId) 444 if cachedInstance == nil { 445 log.Infof("[Health Check][Check]instance %s has been deleted", instanceValue.id) 446 return 447 } 448 request := &plugin.CheckRequest{ 449 QueryRequest: plugin.QueryRequest{ 450 InstanceId: instanceValue.id, 451 Host: instanceValue.host, 452 Port: instanceValue.port, 453 Healthy: cachedInstance.Healthy(), 454 }, 455 CurTimeSec: currentTimeSec, 456 ExpireDurationSec: instanceValue.expireDurationSec, 457 } 458 checkResp, err = instanceValue.checker.Check(request) 459 if err != nil { 460 log.Errorf("[Health Check][Check]fail to check instance %s:%d, id is %s, err is %v", 461 instanceValue.host, instanceValue.port, instanceValue.id, err) 462 return 463 } 464 if !checkResp.StayUnchanged { 465 code := setInsDbStatus(cachedInstance, checkResp.Healthy, checkResp.LastHeartbeatTimeSec) 466 if checkResp.Healthy { 467 // from unhealthy to healthy 468 log.Infof( 469 "[Health Check][Check]instance change from unhealthy to healthy, id is %s, address is %s:%d", 470 instanceValue.id, instanceValue.host, instanceValue.port) 471 } else { 472 // from healthy to unhealthy 473 log.Infof( 474 "[Health Check][Check]instance change from healthy to unhealthy, id is %s, address is %s:%d", 475 instanceValue.id, instanceValue.host, instanceValue.port) 476 } 477 if code != apimodel.Code_ExecuteSuccess { 478 log.Errorf( 479 "[Health Check][Check]fail to update instance, id is %s, address is %s:%d, code is %d", 480 instanceValue.id, instanceValue.host, instanceValue.port, code) 481 } 482 } 483 } 484 485 // DelClient del client from check 486 func (c *CheckScheduler) DelClient(clientWithChecker *ClientWithChecker) { 487 client := clientWithChecker.client 488 clientId := client.Proto().GetId().GetValue() 489 exists := c.delClientIfPresent(clientId) 490 log.Infof("[Health Check][Check]remove check client is %s:%d, id is %s, exists is %v", 491 client.Proto().GetHost().GetValue(), 0, clientId, exists) 492 if exists { 493 c.removeAdopting(clientId, clientWithChecker.checker) 494 } 495 } 496 497 // DelInstance del instance from check 498 func (c *CheckScheduler) DelInstance(instanceWithChecker *InstanceWithChecker) { 499 instance := instanceWithChecker.instance 500 instanceId := instance.ID() 501 exists := c.delInstanceIfPresent(instanceId) 502 log.Infof("[Health Check][Check]remove check instance is %s:%d, id is %s, exists is %v", 503 instance.Host(), instance.Port(), instanceId, exists) 504 if exists { 505 c.removeAdopting(instanceId, instanceWithChecker.checker) 506 } 507 } 508 509 func (c *CheckScheduler) delInstanceIfPresent(instanceId string) bool { 510 c.rwMutex.Lock() 511 defer c.rwMutex.Unlock() 512 _, ok := c.scheduledInstances[instanceId] 513 delete(c.scheduledInstances, instanceId) 514 return ok 515 } 516 517 func (c *CheckScheduler) delClientIfPresent(clientId string) bool { 518 c.rwMutex.Lock() 519 defer c.rwMutex.Unlock() 520 _, ok := c.scheduledClients[clientId] 521 delete(c.scheduledClients, clientId) 522 return ok 523 } 524 525 func (c *CheckScheduler) doCheckClient(ctx context.Context) { 526 log.Infof("[Health Check][Check]client check worker has been started, tick seconds is %d", 527 c.clientCheckIntervalSec) 528 tick := time.NewTicker(time.Duration(c.clientCheckIntervalSec*1000+int64(getRandDelayMilli())) * time.Millisecond) 529 defer tick.Stop() 530 for { 531 select { 532 case <-tick.C: 533 var itemsToCheck []string 534 if len(c.scheduledClients) == 0 { 535 continue 536 } 537 curTimeSec := currentTimeSec() 538 c.rwMutex.RLock() 539 for id, value := range c.scheduledClients { 540 if value.lastCheckTimeSec == 0 { 541 itemsToCheck = append(itemsToCheck, id) 542 } 543 diff := curTimeSec - value.lastCheckTimeSec 544 if diff < 0 || diff >= int64(value.expireDurationSec) { 545 itemsToCheck = append(itemsToCheck, id) 546 } 547 } 548 c.rwMutex.RUnlock() 549 if len(itemsToCheck) == 0 { 550 continue 551 } 552 for _, id := range itemsToCheck { 553 item := c.checkCallbackClient(id) 554 if nil != item { 555 item.lastCheckTimeSec = currentTimeSec() 556 } 557 } 558 timeCost := currentTimeSec() - curTimeSec 559 log.Infof("[Health Check][Check]client check finished, time cost %d, client count %d", 560 timeCost, len(itemsToCheck)) 561 case <-ctx.Done(): 562 log.Infof("[Health Check][Check]client check worker has been stopped") 563 return 564 } 565 } 566 } 567 568 // setInsDbStatus 修改实例状态, 需要打印操作记录 569 func setInsDbStatus(instance *model.Instance, healthStatus bool, lastBeatTime int64) apimodel.Code { 570 id := instance.ID() 571 host := instance.Host() 572 port := instance.Port() 573 log.Infof("[Health Check][Check]addr:%s:%d id:%s set db status %v", host, port, id, healthStatus) 574 575 var code apimodel.Code 576 if server.bc.HeartbeatOpen() { 577 code = asyncSetInsDbStatus(instance.Proto, healthStatus, lastBeatTime) 578 } else { 579 code = serialSetInsDbStatus(instance.Proto, healthStatus, lastBeatTime) 580 } 581 if code != apimodel.Code_ExecuteSuccess { 582 return code 583 } 584 585 // 这里为了避免多次发送重复的事件,对实例原本的health 状态以及 healthStatus 状态进行对比,不一致才 586 // 发布服务实例变更事件 587 if instance.Healthy() != healthStatus { 588 event := model.InstanceEvent{ 589 Id: id, 590 Namespace: instance.Namespace(), 591 Service: instance.Service(), 592 Instance: instance.Proto, 593 } 594 595 // 实例状态变化进行 DiscoverEvent 输出 596 if healthStatus { 597 event.EType = model.EventInstanceTurnHealth 598 } else { 599 event.EType = model.EventInstanceTurnUnHealth 600 } 601 602 server.publishInstanceEvent(instance.ServiceID, event) 603 } 604 605 return code 606 } 607 608 // asyncSetInsDbStatus 异步新建实例 609 // 底层函数会合并delete请求,增加并发创建的吞吐 610 // req 原始请求 611 // ins 包含了req数据与instanceID,serviceToken 612 func asyncSetInsDbStatus(ins *apiservice.Instance, healthStatus bool, lastBeatTime int64) apimodel.Code { 613 future := server.bc.AsyncHeartbeat(ins, healthStatus, lastBeatTime) 614 if err := future.Wait(); err != nil { 615 log.Error(err.Error()) 616 } 617 return future.Code() 618 } 619 620 // asyncDeleteClient 异步软删除客户端 621 // 底层函数会合并delete请求,增加并发创建的吞吐 622 // req 原始请求 623 // ins 包含了req数据与instanceID,serviceToken 624 func asyncDeleteClient(client *apiservice.Client) apimodel.Code { 625 future := server.bc.AsyncDeregisterClient(client) 626 if err := future.Wait(); err != nil { 627 log.Error("[Health Check][Check] async delete client", zap.String("client-id", client.GetId().GetValue()), 628 zap.Error(err)) 629 } 630 return future.Code() 631 } 632 633 // serialSetInsDbStatus 同步串行创建实例 634 // req为原始的请求体 635 // ins包括了req的内容,并且填充了instanceID与serviceToken 636 func serialSetInsDbStatus(ins *apiservice.Instance, healthStatus bool, lastBeatTime int64) apimodel.Code { 637 id := ins.GetId().GetValue() 638 err := server.storage.SetInstanceHealthStatus(id, model.StatusBoolToInt(healthStatus), utils.NewUUID()) 639 if err != nil { 640 log.Errorf("[Health Check][Check]id: %s set db status err:%s", id, err) 641 return commonstore.StoreCode2APICode(err) 642 } 643 return apimodel.Code_ExecuteSuccess 644 }