github.com/nacos-group/nacos-sdk-go@v1.1.4/clients/naming_client/host_reactor.go (about) 1 /* 2 * Copyright 1999-2020 Alibaba Group Holding Ltd. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package naming_client 18 19 import ( 20 "encoding/json" 21 "errors" 22 "fmt" 23 "reflect" 24 "sort" 25 "strconv" 26 "strings" 27 "time" 28 29 "github.com/nacos-group/nacos-sdk-go/clients/cache" 30 "github.com/nacos-group/nacos-sdk-go/common/logger" 31 "github.com/nacos-group/nacos-sdk-go/model" 32 "github.com/nacos-group/nacos-sdk-go/util" 33 ) 34 35 type HostReactor struct { 36 serviceInfoMap cache.ConcurrentMap 37 cacheDir string 38 updateThreadNum int 39 serviceProxy NamingProxy 40 pushReceiver PushReceiver 41 subCallback SubscribeCallback 42 updateTimeMap cache.ConcurrentMap 43 updateCacheWhenEmpty bool 44 } 45 46 const Default_Update_Thread_Num = 20 47 48 func NewHostReactor(serviceProxy NamingProxy, cacheDir string, updateThreadNum int, notLoadCacheAtStart bool, subCallback SubscribeCallback, updateCacheWhenEmpty bool) HostReactor { 49 if updateThreadNum <= 0 { 50 updateThreadNum = Default_Update_Thread_Num 51 } 52 hr := HostReactor{ 53 serviceProxy: serviceProxy, 54 cacheDir: cacheDir, 55 updateThreadNum: updateThreadNum, 56 serviceInfoMap: cache.NewConcurrentMap(), 57 subCallback: subCallback, 58 updateTimeMap: cache.NewConcurrentMap(), 59 updateCacheWhenEmpty: updateCacheWhenEmpty, 60 } 61 pr := NewPushReceiver(&hr) 62 hr.pushReceiver = *pr 63 if !notLoadCacheAtStart { 64 hr.loadCacheFromDisk() 65 } 66 go hr.asyncUpdateService() 67 return hr 68 } 69 70 func (hr *HostReactor) loadCacheFromDisk() { 71 serviceMap := cache.ReadServicesFromFile(hr.cacheDir) 72 if len(serviceMap) == 0 { 73 return 74 } 75 for k, v := range serviceMap { 76 hr.serviceInfoMap.Set(k, v) 77 } 78 } 79 80 func (hr *HostReactor) ProcessServiceJson(result string) { 81 service := util.JsonToService(result) 82 if service == nil { 83 return 84 } 85 cacheKey := util.GetServiceCacheKey(service.Name, service.Clusters) 86 87 oldDomain, ok := hr.serviceInfoMap.Get(cacheKey) 88 if ok && !hr.updateCacheWhenEmpty { 89 //if instance list is empty,not to update cache 90 if service.Hosts == nil || len(service.Hosts) == 0 { 91 logger.Errorf("do not have useful host, ignore it, name:%s", service.Name) 92 return 93 } 94 } 95 hr.updateTimeMap.Set(cacheKey, uint64(util.CurrentMillis())) 96 hr.serviceInfoMap.Set(cacheKey, *service) 97 oldService, serviceOk := oldDomain.(model.Service) 98 if !ok || ok && serviceOk && isServiceInstanceChanged(&oldService, service) { 99 if !ok { 100 logger.Info("service not found in cache " + cacheKey) 101 } else { 102 logger.Info("service key:%s was updated to:%s", cacheKey, util.ToJsonString(service)) 103 } 104 cache.WriteServicesToFile(*service, hr.cacheDir) 105 hr.subCallback.ServiceChanged(service) 106 } 107 } 108 109 func (hr *HostReactor) GetServiceInfo(serviceName string, clusters string) (model.Service, error) { 110 key := util.GetServiceCacheKey(serviceName, clusters) 111 cacheService, ok := hr.serviceInfoMap.Get(key) 112 if !ok { 113 hr.updateServiceNow(serviceName, clusters) 114 if cacheService, ok = hr.serviceInfoMap.Get(key); !ok { 115 return model.Service{}, errors.New("get service info failed") 116 } 117 } 118 119 return cacheService.(model.Service), nil 120 } 121 122 func (hr *HostReactor) GetAllServiceInfo(nameSpace, groupName string, pageNo, pageSize uint32) (model.ServiceList, error) { 123 data := model.ServiceList{} 124 result, err := hr.serviceProxy.GetAllServiceInfoList(nameSpace, groupName, pageNo, pageSize) 125 if err != nil { 126 logger.Errorf("GetAllServiceInfoList return error!nameSpace:%s groupName:%s pageNo:%d, pageSize:%d err:%+v", 127 nameSpace, groupName, pageNo, pageSize, err) 128 return data, err 129 } 130 if result == "" { 131 logger.Warnf("GetAllServiceInfoList result is empty!nameSpace:%s groupName:%s pageNo:%d, pageSize:%d", 132 nameSpace, groupName, pageNo, pageSize) 133 return data, nil 134 } 135 136 err = json.Unmarshal([]byte(result), &data) 137 if err != nil { 138 logger.Errorf("GetAllServiceInfoList result json.Unmarshal error!nameSpace:%s groupName:%s pageNo:%d, pageSize:%d", 139 nameSpace, groupName, pageNo, pageSize) 140 return data, err 141 } 142 return data, nil 143 } 144 145 func (hr *HostReactor) updateServiceNow(serviceName, clusters string) { 146 result, err := hr.serviceProxy.QueryList(serviceName, clusters, hr.pushReceiver.port, false) 147 148 if err != nil { 149 logger.Errorf("QueryList return error!serviceName:%s cluster:%s err:%+v", serviceName, clusters, err) 150 return 151 } 152 if result == "" { 153 logger.Errorf("QueryList result is empty!serviceName:%s cluster:%s", serviceName, clusters) 154 return 155 } 156 hr.ProcessServiceJson(result) 157 } 158 159 func (hr *HostReactor) asyncUpdateService() { 160 sema := util.NewSemaphore(hr.updateThreadNum) 161 for { 162 for _, v := range hr.serviceInfoMap.Items() { 163 service := v.(model.Service) 164 lastRefTime, ok := hr.updateTimeMap.Get(util.GetServiceCacheKey(service.Name, service.Clusters)) 165 if !ok { 166 lastRefTime = uint64(0) 167 } 168 if uint64(util.CurrentMillis())-lastRefTime.(uint64) > service.CacheMillis { 169 sema.Acquire() 170 go func() { 171 hr.updateServiceNow(service.Name, service.Clusters) 172 sema.Release() 173 }() 174 } 175 } 176 time.Sleep(1 * time.Second) 177 } 178 } 179 180 // return true when service instance changed ,otherwise return false. 181 func isServiceInstanceChanged(oldService, newService *model.Service) bool { 182 oldHostsLen := len(oldService.Hosts) 183 newHostsLen := len(newService.Hosts) 184 if oldHostsLen != newHostsLen { 185 return true 186 } 187 // compare refTime 188 oldRefTime := oldService.LastRefTime 189 newRefTime := newService.LastRefTime 190 if oldRefTime > newRefTime { 191 logger.Warn(fmt.Sprintf("out of date data received, old-t: %v , new-t: %v", oldRefTime, newRefTime)) 192 return false 193 } 194 // sort instance list 195 oldInstance := oldService.Hosts 196 newInstance := make([]model.Instance, len(newService.Hosts)) 197 copy(newInstance, newService.Hosts) 198 sortInstance(oldInstance) 199 sortInstance(newInstance) 200 return !reflect.DeepEqual(oldInstance, newInstance) 201 } 202 203 type instanceSorter []model.Instance 204 205 func (s instanceSorter) Len() int { 206 return len(s) 207 } 208 func (s instanceSorter) Swap(i, j int) { 209 s[i], s[j] = s[j], s[i] 210 } 211 func (s instanceSorter) Less(i, j int) bool { 212 insI, insJ := s[i], s[j] 213 // using ip and port to sort 214 ipNum1, _ := strconv.Atoi(strings.ReplaceAll(insI.Ip, ".", "")) 215 ipNum2, _ := strconv.Atoi(strings.ReplaceAll(insJ.Ip, ".", "")) 216 if ipNum1 < ipNum2 { 217 return true 218 } 219 if insI.Port < insJ.Port { 220 return true 221 } 222 return false 223 } 224 225 // sort instances 226 func sortInstance(instances []model.Instance) { 227 sort.Sort(instanceSorter(instances)) 228 }