github.com/polarismesh/polaris@v1.17.8/service/healthcheck/dispatch.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  	"sync/atomic"
    24  	"time"
    25  
    26  	apiservice "github.com/polarismesh/specification/source/go/api/v1/service_manage"
    27  
    28  	commonhash "github.com/polarismesh/polaris/common/hash"
    29  	"github.com/polarismesh/polaris/common/model"
    30  )
    31  
    32  const (
    33  	// eventInterval, trigger after instance change event
    34  	eventInterval = 5 * time.Second
    35  	// ensureInterval, trigger when timeout
    36  	ensureInterval = 61 * time.Second
    37  )
    38  
    39  // Dispatcher dispatch all instances using consistent hash ring
    40  type Dispatcher struct {
    41  	svr *Server
    42  
    43  	healthCheckInstancesChanged uint32
    44  	healthCheckClientsChanged   uint32
    45  	selfServiceInstancesChanged uint32
    46  	managedInstances            map[string]*InstanceWithChecker
    47  	managedClients              map[string]*ClientWithChecker
    48  
    49  	selfServiceBuckets map[commonhash.Bucket]bool
    50  	continuum          *commonhash.Continuum
    51  	mutex              *sync.Mutex
    52  
    53  	noAvailableServers bool
    54  }
    55  
    56  func newDispatcher(ctx context.Context, svr *Server) *Dispatcher {
    57  	dispatcher := &Dispatcher{
    58  		svr:   svr,
    59  		mutex: &sync.Mutex{},
    60  	}
    61  	return dispatcher
    62  }
    63  
    64  // UpdateStatusByEvent 更新变更状态
    65  func (d *Dispatcher) UpdateStatusByEvent(event CacheEvent) {
    66  	d.mutex.Lock()
    67  	defer d.mutex.Unlock()
    68  	if event.selfServiceInstancesChanged {
    69  		atomic.StoreUint32(&d.selfServiceInstancesChanged, 1)
    70  	}
    71  	if event.healthCheckInstancesChanged {
    72  		atomic.StoreUint32(&d.healthCheckInstancesChanged, 1)
    73  	}
    74  	if event.healthCheckClientChanged {
    75  		atomic.StoreUint32(&d.healthCheckClientsChanged, 1)
    76  	}
    77  }
    78  
    79  // startDispatchingJob start job to dispatch instances
    80  func (d *Dispatcher) startDispatchingJob(ctx context.Context) {
    81  	go func() {
    82  		eventTicker := time.NewTicker(eventInterval)
    83  		defer eventTicker.Stop()
    84  		ensureTicker := time.NewTicker(ensureInterval)
    85  		defer ensureTicker.Stop()
    86  
    87  		for {
    88  			select {
    89  			case <-eventTicker.C:
    90  				d.processEvent()
    91  			case <-ensureTicker.C:
    92  				d.processEnsure()
    93  			case <-ctx.Done():
    94  				return
    95  			}
    96  		}
    97  	}()
    98  }
    99  
   100  const weight = 100
   101  
   102  func compareBuckets(src map[commonhash.Bucket]bool, dst map[commonhash.Bucket]bool) bool {
   103  	if len(src) != len(dst) {
   104  		return false
   105  	}
   106  	if len(src) == 0 {
   107  		return false
   108  	}
   109  	for bucket := range dst {
   110  		if _, ok := src[bucket]; !ok {
   111  			return false
   112  		}
   113  	}
   114  	return true
   115  }
   116  
   117  func (d *Dispatcher) reloadSelfContinuum() bool {
   118  	nextBuckets := make(map[commonhash.Bucket]bool)
   119  	d.svr.cacheProvider.RangeSelfServiceInstances(func(instance *apiservice.Instance) {
   120  		if instance.GetIsolate().GetValue() || !instance.GetHealthy().GetValue() {
   121  			return
   122  		}
   123  		nextBuckets[commonhash.Bucket{
   124  			Host:   instance.GetHost().GetValue(),
   125  			Weight: weight,
   126  		}] = true
   127  	})
   128  	if len(nextBuckets) == 0 {
   129  		d.noAvailableServers = true
   130  	}
   131  	originBucket := d.selfServiceBuckets
   132  	log.Debugf("[Health Check][Dispatcher]reload continuum by %v, origin is %v", nextBuckets, originBucket)
   133  	if compareBuckets(originBucket, nextBuckets) {
   134  		return false
   135  	}
   136  	if d.noAvailableServers && len(nextBuckets) > 0 {
   137  		// no available buckets, we need to suspend all the checkers
   138  		for _, checker := range d.svr.checkers {
   139  			checker.Suspend()
   140  		}
   141  		d.noAvailableServers = false
   142  	}
   143  	d.selfServiceBuckets = nextBuckets
   144  	d.continuum = commonhash.New(d.selfServiceBuckets)
   145  	return true
   146  }
   147  
   148  func (d *Dispatcher) reloadManagedClients() {
   149  	nextClients := make(map[string]*ClientWithChecker)
   150  
   151  	if d.continuum != nil {
   152  		d.svr.cacheProvider.RangeHealthCheckClients(func(itemChecker ItemWithChecker, client *model.Client) {
   153  			clientId := client.Proto().GetId().GetValue()
   154  			host := d.continuum.Hash(itemChecker.GetHashValue())
   155  			if host == d.svr.localHost {
   156  				nextClients[clientId] = itemChecker.(*ClientWithChecker)
   157  			}
   158  		})
   159  	}
   160  	log.Infof("[Health Check][Dispatcher]count %d clients has been dispatched to %s, total is %d",
   161  		len(nextClients), d.svr.localHost, d.svr.cacheProvider.healthCheckInstances.Count())
   162  	originClients := d.managedClients
   163  	d.managedClients = nextClients
   164  	if len(nextClients) > 0 {
   165  		for id, client := range nextClients {
   166  			if len(originClients) == 0 {
   167  				d.svr.checkScheduler.AddClient(client)
   168  				continue
   169  			}
   170  			if _, ok := originClients[id]; !ok {
   171  				d.svr.checkScheduler.AddClient(client)
   172  			}
   173  		}
   174  	}
   175  	if len(originClients) > 0 {
   176  		for id, client := range originClients {
   177  			if len(nextClients) == 0 {
   178  				d.svr.checkScheduler.DelClient(client)
   179  				continue
   180  			}
   181  			if _, ok := nextClients[id]; !ok {
   182  				d.svr.checkScheduler.DelClient(client)
   183  			}
   184  		}
   185  	}
   186  }
   187  
   188  func (d *Dispatcher) reloadManagedInstances() {
   189  	nextInstances := make(map[string]*InstanceWithChecker)
   190  	if d.continuum != nil {
   191  		d.svr.cacheProvider.RangeHealthCheckInstances(func(itemChecker ItemWithChecker, instance *model.Instance) {
   192  			instanceId := instance.ID()
   193  			host := d.continuum.Hash(itemChecker.GetHashValue())
   194  			if host == d.svr.localHost {
   195  				nextInstances[instanceId] = itemChecker.(*InstanceWithChecker)
   196  			}
   197  		})
   198  	}
   199  	log.Infof("[Health Check][Dispatcher]count %d instances has been dispatched to %s, total is %d",
   200  		len(nextInstances), d.svr.localHost, d.svr.cacheProvider.healthCheckInstances.Count())
   201  	originInstances := d.managedInstances
   202  	d.managedInstances = nextInstances
   203  	if len(nextInstances) > 0 {
   204  		for _, instance := range nextInstances {
   205  			d.svr.checkScheduler.UpsertInstance(instance)
   206  		}
   207  	}
   208  	if len(originInstances) > 0 {
   209  		for id, instance := range originInstances {
   210  			if len(nextInstances) == 0 {
   211  				d.svr.checkScheduler.DelInstance(instance)
   212  				continue
   213  			}
   214  			if _, ok := nextInstances[id]; !ok {
   215  				d.svr.checkScheduler.DelInstance(instance)
   216  			}
   217  		}
   218  	}
   219  }
   220  
   221  func (d *Dispatcher) processEvent() {
   222  	var selfContinuumReloaded bool
   223  	if atomic.CompareAndSwapUint32(&d.selfServiceInstancesChanged, 1, 0) {
   224  		selfContinuumReloaded = d.reloadSelfContinuum()
   225  	}
   226  	if selfContinuumReloaded || atomic.CompareAndSwapUint32(&d.healthCheckInstancesChanged, 1, 0) {
   227  		d.reloadManagedInstances()
   228  	}
   229  	if selfContinuumReloaded || atomic.CompareAndSwapUint32(&d.healthCheckClientsChanged, 1, 0) {
   230  		d.reloadManagedClients()
   231  	}
   232  }
   233  
   234  func (d *Dispatcher) processEnsure() {
   235  	d.reloadSelfContinuum()
   236  	d.reloadManagedInstances()
   237  	d.reloadManagedClients()
   238  }