dubbo.apache.org/dubbo-go/v3@v3.1.1/registry/zookeeper/service_discovery.go (about) 1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package zookeeper 19 20 import ( 21 "fmt" 22 "strconv" 23 "strings" 24 "sync" 25 ) 26 27 import ( 28 gxset "github.com/dubbogo/gost/container/set" 29 gxzookeeper "github.com/dubbogo/gost/database/kv/zk" 30 gxpage "github.com/dubbogo/gost/hash/page" 31 "github.com/dubbogo/gost/log/logger" 32 ) 33 34 import ( 35 "dubbo.apache.org/dubbo-go/v3/common" 36 "dubbo.apache.org/dubbo-go/v3/common/constant" 37 "dubbo.apache.org/dubbo-go/v3/common/extension" 38 "dubbo.apache.org/dubbo-go/v3/registry" 39 "dubbo.apache.org/dubbo-go/v3/remoting" 40 "dubbo.apache.org/dubbo-go/v3/remoting/zookeeper" 41 "dubbo.apache.org/dubbo-go/v3/remoting/zookeeper/curator_discovery" 42 ) 43 44 const ( 45 rootPath = "/services" 46 ) 47 48 func init() { 49 extension.SetServiceDiscovery(constant.ZookeeperKey, newZookeeperServiceDiscovery) 50 } 51 52 type zookeeperServiceDiscovery struct { 53 client *gxzookeeper.ZookeeperClient 54 csd *curator_discovery.ServiceDiscovery 55 url *common.URL 56 wg sync.WaitGroup 57 cltLock sync.Mutex 58 listenLock sync.Mutex 59 done chan struct{} 60 rootPath string 61 listenNames []string 62 instanceListenerMap map[string]*gxset.HashSet 63 } 64 65 // newZookeeperServiceDiscovery the constructor of newZookeeperServiceDiscovery 66 func newZookeeperServiceDiscovery(url *common.URL) (registry.ServiceDiscovery, error) { 67 group := url.GetParam(constant.RegistryGroupKey, rootPath) 68 if !strings.HasPrefix(group, constant.PathSeparator) { 69 group = constant.PathSeparator + group 70 } 71 72 zksd := &zookeeperServiceDiscovery{ 73 url: url, 74 rootPath: group, 75 instanceListenerMap: make(map[string]*gxset.HashSet), 76 } 77 if err := zookeeper.ValidateZookeeperClient(zksd, url.Location); err != nil { 78 return nil, err 79 } 80 zksd.WaitGroup().Add(1) // zk client start successful, then wg +1 81 go zookeeper.HandleClientRestart(zksd) 82 zksd.csd = curator_discovery.NewServiceDiscovery(zksd.client, group) 83 return zksd, nil 84 } 85 86 // nolint 87 func (zksd *zookeeperServiceDiscovery) ZkClient() *gxzookeeper.ZookeeperClient { 88 return zksd.client 89 } 90 91 // nolint 92 func (zksd *zookeeperServiceDiscovery) SetZkClient(client *gxzookeeper.ZookeeperClient) { 93 zksd.client = client 94 } 95 96 // nolint 97 func (zksd *zookeeperServiceDiscovery) ZkClientLock() *sync.Mutex { 98 return &zksd.cltLock 99 } 100 101 // nolint 102 func (zksd *zookeeperServiceDiscovery) WaitGroup() *sync.WaitGroup { 103 return &zksd.wg 104 } 105 106 // nolint 107 func (zksd *zookeeperServiceDiscovery) Done() chan struct{} { 108 return zksd.done 109 } 110 111 // RestartCallBack when zookeeper connection reconnect this function will be invoked. 112 // try to re-register service, and listen services 113 func (zksd *zookeeperServiceDiscovery) RestartCallBack() bool { 114 zksd.csd.ReRegisterServices() 115 zksd.listenLock.Lock() 116 defer zksd.listenLock.Unlock() 117 for _, name := range zksd.listenNames { 118 zksd.csd.ListenServiceEvent(name, zksd) 119 } 120 return true 121 } 122 123 // nolint 124 func (zksd *zookeeperServiceDiscovery) GetURL() *common.URL { 125 return zksd.url 126 } 127 128 // nolint 129 func (zksd *zookeeperServiceDiscovery) String() string { 130 return fmt.Sprintf("zookeeper-service-discovery[%s]", zksd.url) 131 } 132 133 // Destroy will destroy the clinet. 134 func (zksd *zookeeperServiceDiscovery) Destroy() error { 135 zksd.csd.Close() 136 return nil 137 } 138 139 // Register will register service in zookeeper, instance convert to curator's service instance 140 // which define in curator-x-discovery. 141 func (zksd *zookeeperServiceDiscovery) Register(instance registry.ServiceInstance) error { 142 cris := zksd.toCuratorInstance(instance) 143 return zksd.csd.RegisterService(cris) 144 } 145 146 // Update will update service in zookeeper, instance convert to curator's service instance 147 // which define in curator-x-discovery, please refer to https://github.com/apache/curator. 148 func (zksd *zookeeperServiceDiscovery) Update(instance registry.ServiceInstance) error { 149 cris := zksd.toCuratorInstance(instance) 150 return zksd.csd.UpdateService(cris) 151 } 152 153 // Unregister will unregister the instance in zookeeper 154 func (zksd *zookeeperServiceDiscovery) Unregister(instance registry.ServiceInstance) error { 155 cris := zksd.toCuratorInstance(instance) 156 return zksd.csd.UnregisterService(cris) 157 } 158 159 // GetDefaultPageSize will return the constant registry.DefaultPageSize 160 func (zksd *zookeeperServiceDiscovery) GetDefaultPageSize() int { 161 return registry.DefaultPageSize 162 } 163 164 // GetServices will return the all services in zookeeper 165 func (zksd *zookeeperServiceDiscovery) GetServices() *gxset.HashSet { 166 services, err := zksd.csd.QueryForNames() 167 res := gxset.NewSet() 168 if err != nil { 169 logger.Errorf("[zkServiceDiscovery] Could not query the services: %v", err) 170 return res 171 } 172 for _, service := range services { 173 res.Add(service) 174 } 175 return res 176 } 177 178 // GetInstances will return the instances in a service 179 func (zksd *zookeeperServiceDiscovery) GetInstances(serviceName string) []registry.ServiceInstance { 180 criss, err := zksd.csd.QueryForInstances(serviceName) 181 if err != nil { 182 logger.Errorf("[zkServiceDiscovery] Could not query the instances for service{%s}, error = err{%v} ", 183 serviceName, err) 184 return make([]registry.ServiceInstance, 0) 185 } 186 iss := make([]registry.ServiceInstance, 0, len(criss)) 187 for _, cris := range criss { 188 iss = append(iss, toZookeeperInstance(cris)) 189 } 190 return iss 191 } 192 193 // GetInstancesByPage will return the instances 194 func (zksd *zookeeperServiceDiscovery) GetInstancesByPage(serviceName string, offset int, pageSize int) gxpage.Pager { 195 all := zksd.GetInstances(serviceName) 196 res := make([]interface{}, 0, pageSize) 197 // could not use res = all[a:b] here because the res should be []interface{}, not []ServiceInstance 198 for i := offset; i < len(all) && i < offset+pageSize; i++ { 199 res = append(res, all[i]) 200 } 201 return gxpage.NewPage(offset, pageSize, res, len(all)) 202 } 203 204 // GetHealthyInstancesByPage will return the instance 205 // In zookeeper, all service instance's is healthy. 206 // However, the healthy parameter in this method maybe false. So we can not use that API. 207 // Thus, we must query all instances and then do filter 208 func (zksd *zookeeperServiceDiscovery) GetHealthyInstancesByPage(serviceName string, offset int, pageSize int, healthy bool) gxpage.Pager { 209 all := zksd.GetInstances(serviceName) 210 res := make([]interface{}, 0, pageSize) 211 // could not use res = all[a:b] here because the res should be []interface{}, not []ServiceInstance 212 var ( 213 i = offset 214 count = 0 215 ) 216 for i < len(all) && count < pageSize { 217 ins := all[i] 218 if ins.IsHealthy() == healthy { 219 res = append(res, all[i]) 220 count++ 221 } 222 i++ 223 } 224 return gxpage.NewPage(offset, pageSize, res, len(all)) 225 } 226 227 // GetRequestInstances will return the instances 228 func (zksd *zookeeperServiceDiscovery) GetRequestInstances(serviceNames []string, offset int, requestedSize int) map[string]gxpage.Pager { 229 res := make(map[string]gxpage.Pager, len(serviceNames)) 230 for _, name := range serviceNames { 231 res[name] = zksd.GetInstancesByPage(name, offset, requestedSize) 232 } 233 return res 234 } 235 236 // AddListener ListenServiceEvent will add a data listener in service 237 func (zksd *zookeeperServiceDiscovery) AddListener(listener registry.ServiceInstancesChangedListener) error { 238 zksd.listenLock.Lock() 239 defer zksd.listenLock.Unlock() 240 241 for _, t := range listener.GetServiceNames().Values() { 242 serviceName, ok := t.(string) 243 if !ok { 244 logger.Errorf("service name error %s", t) 245 continue 246 } 247 zksd.listenNames = append(zksd.listenNames, serviceName) 248 listenerSet, found := zksd.instanceListenerMap[serviceName] 249 if !found { 250 listenerSet = gxset.NewSet(listener) 251 listenerSet.Add(listener) 252 zksd.instanceListenerMap[serviceName] = listenerSet 253 } else { 254 listenerSet.Add(listener) 255 } 256 } 257 258 for _, t := range listener.GetServiceNames().Values() { 259 serviceName, ok := t.(string) 260 if !ok { 261 logger.Errorf("service name error %s", t) 262 continue 263 } 264 zksd.csd.ListenServiceEvent(serviceName, zksd) 265 } 266 return nil 267 } 268 269 // DataChange implement DataListener's DataChange function 270 // to resolve event to do DispatchEventByServiceName 271 func (zksd *zookeeperServiceDiscovery) DataChange(eventType remoting.Event) bool { 272 path := strings.TrimPrefix(eventType.Path, zksd.rootPath) 273 path = strings.TrimPrefix(path, constant.PathSeparator) 274 // get service name in zk path 275 serviceName := strings.Split(path, constant.PathSeparator)[0] 276 277 var err error 278 instances := zksd.GetInstances(serviceName) 279 for _, lis := range zksd.instanceListenerMap[serviceName].Values() { 280 var instanceListener registry.ServiceInstancesChangedListener 281 instanceListener = lis.(registry.ServiceInstancesChangedListener) 282 err = instanceListener.OnEvent(registry.NewServiceInstancesChangedEvent(serviceName, instances)) 283 } 284 285 if err != nil { 286 logger.Errorf("[zkServiceDiscovery] DispatchEventByServiceName{%s} error = err{%v}", serviceName, err) 287 return false 288 } 289 return true 290 } 291 292 // toCuratorInstance convert to curator's service instance 293 func (zksd *zookeeperServiceDiscovery) toCuratorInstance(instance registry.ServiceInstance) *curator_discovery.ServiceInstance { 294 id := instance.GetHost() + ":" + strconv.Itoa(instance.GetPort()) 295 pl := make(map[string]interface{}, 8) 296 pl["id"] = id 297 pl["name"] = instance.GetServiceName() 298 pl["metadata"] = instance.GetMetadata() 299 pl["@class"] = "org.apache.dubbo.registry.zookeeper.ZookeeperInstance" 300 cuis := &curator_discovery.ServiceInstance{ 301 Name: instance.GetServiceName(), 302 ID: id, 303 Address: instance.GetHost(), 304 Port: instance.GetPort(), 305 Payload: pl, 306 RegistrationTimeUTC: 0, 307 Tag: instance.GetTag(), 308 } 309 return cuis 310 } 311 312 // toZookeeperInstance convert to registry's service instance 313 func toZookeeperInstance(cris *curator_discovery.ServiceInstance) registry.ServiceInstance { 314 pl, ok := cris.Payload.(map[string]interface{}) 315 if !ok { 316 logger.Errorf("[zkServiceDiscovery] toZookeeperInstance{%s} payload is not map[string]interface{}", cris.ID) 317 return nil 318 } 319 mdi, ok := pl["metadata"].(map[string]interface{}) 320 if !ok { 321 logger.Errorf("[zkServiceDiscovery] toZookeeperInstance{%s} metadata is not map[string]interface{}", cris.ID) 322 return nil 323 } 324 md := make(map[string]string, len(mdi)) 325 for k, v := range mdi { 326 md[k] = fmt.Sprint(v) 327 } 328 return ®istry.DefaultServiceInstance{ 329 ID: cris.ID, 330 ServiceName: cris.Name, 331 Host: cris.Address, 332 Port: cris.Port, 333 Enable: true, 334 Healthy: true, 335 Metadata: md, 336 Tag: cris.Tag, 337 } 338 }