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 }