github.com/polarismesh/polaris@v1.17.8/service/healthcheck/leader.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  	"time"
    23  
    24  	apimodel "github.com/polarismesh/specification/source/go/api/v1/model"
    25  
    26  	"github.com/polarismesh/polaris/common/model"
    27  	"github.com/polarismesh/polaris/plugin"
    28  	"github.com/polarismesh/polaris/store"
    29  )
    30  
    31  // LeaderChangeEventHandler process the event when server act as leader
    32  type LeaderChangeEventHandler struct {
    33  	cacheProvider    *CacheProvider
    34  	ctx              context.Context
    35  	cancel           context.CancelFunc
    36  	minCheckInterval time.Duration
    37  }
    38  
    39  // newLeaderChangeEventHandler
    40  func newLeaderChangeEventHandler(cacheProvider *CacheProvider,
    41  	minCheckInterval time.Duration) *LeaderChangeEventHandler {
    42  
    43  	return &LeaderChangeEventHandler{
    44  		cacheProvider:    cacheProvider,
    45  		minCheckInterval: minCheckInterval,
    46  	}
    47  }
    48  
    49  // PreProcess do preprocess logic for event
    50  func (handler *LeaderChangeEventHandler) PreProcess(ctx context.Context, value any) any {
    51  	return value
    52  }
    53  
    54  // OnEvent event trigger
    55  func (handler *LeaderChangeEventHandler) OnEvent(ctx context.Context, i interface{}) error {
    56  	e := i.(store.LeaderChangeEvent)
    57  	if e.Key != store.ElectionKeySelfServiceChecker {
    58  		return nil
    59  	}
    60  
    61  	if e.Leader {
    62  		handler.startCheckSelfServiceInstances()
    63  	} else {
    64  		handler.stopCheckSelfServiceInstances()
    65  	}
    66  	return nil
    67  }
    68  
    69  // startCheckSelfServiceInstances
    70  func (handler *LeaderChangeEventHandler) startCheckSelfServiceInstances() {
    71  	if handler.ctx != nil {
    72  		log.Warn("[healthcheck] receive unexpected leader state event")
    73  		return
    74  	}
    75  
    76  	ctx, cancel := context.WithCancel(context.Background())
    77  	handler.ctx = ctx
    78  	handler.cancel = cancel
    79  	go func() {
    80  		log.Info("[healthcheck] i am leader, start check health of selfService instances")
    81  		ticker := time.NewTicker(handler.minCheckInterval)
    82  		defer ticker.Stop()
    83  		for {
    84  			select {
    85  			case <-ticker.C:
    86  				handler.cacheProvider.selfServiceInstances.Range(func(instanceId string, value ItemWithChecker) {
    87  					handler.doCheckSelfServiceInstance(value.GetInstance())
    88  				})
    89  			case <-ctx.Done():
    90  				log.Info("[healthcheck] stop check health of selfService instances")
    91  				return
    92  			}
    93  		}
    94  	}()
    95  }
    96  
    97  // startCheckSelfServiceInstances
    98  func (handler *LeaderChangeEventHandler) stopCheckSelfServiceInstances() {
    99  	if handler.ctx == nil {
   100  		return
   101  	}
   102  	handler.cancel()
   103  	handler.ctx = nil
   104  	handler.cancel = nil
   105  }
   106  
   107  // startCheckSelfServiceInstances
   108  func (handler *LeaderChangeEventHandler) doCheckSelfServiceInstance(cachedInstance *model.Instance) {
   109  	hcEnable, checker := handler.cacheProvider.isHealthCheckEnable(cachedInstance.Proto)
   110  	if !hcEnable {
   111  		log.Warnf("[Health Check][Check] selfService instance %s:%d not enable healthcheck",
   112  			cachedInstance.Host(), cachedInstance.Port())
   113  		return
   114  	}
   115  
   116  	request := &plugin.CheckRequest{
   117  		QueryRequest: plugin.QueryRequest{
   118  			InstanceId: cachedInstance.ID(),
   119  			Host:       cachedInstance.Host(),
   120  			Port:       cachedInstance.Port(),
   121  			Healthy:    cachedInstance.Healthy(),
   122  		},
   123  		CurTimeSec:        currentTimeSec,
   124  		ExpireDurationSec: getExpireDurationSec(cachedInstance.Proto),
   125  	}
   126  	checkResp, err := checker.Check(request)
   127  	if err != nil {
   128  		log.Errorf("[Health Check][Check]fail to check selfService instance %s:%d, id is %s, err is %v",
   129  			cachedInstance.Host(), cachedInstance.Port(), cachedInstance.ID(), err)
   130  		return
   131  	}
   132  	if !checkResp.StayUnchanged {
   133  		code := setInsDbStatus(cachedInstance, checkResp.Healthy, checkResp.LastHeartbeatTimeSec)
   134  		if checkResp.Healthy {
   135  			// from unhealthy to healthy
   136  			log.Infof(
   137  				"[Health Check][Check]selfService instance change from unhealthy to healthy, id is %s, address is %s:%d",
   138  				cachedInstance.ID(), cachedInstance.Host(), cachedInstance.Port())
   139  		} else {
   140  			// from healthy to unhealthy
   141  			log.Infof(
   142  				"[Health Check][Check]selfService instance change from healthy to unhealthy, id is %s, address is %s:%d",
   143  				cachedInstance.ID(), cachedInstance.Host(), cachedInstance.Port())
   144  		}
   145  		if code != apimodel.Code_ExecuteSuccess {
   146  			log.Errorf(
   147  				"[Health Check][Check]fail to update selfService instance, id is %s, address is %s:%d, code is %d",
   148  				cachedInstance.ID(), cachedInstance.Host(), cachedInstance.Port(), code)
   149  		}
   150  	}
   151  }