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  }