dubbo.apache.org/dubbo-go/v3@v3.1.1/registry/nacos/registry.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 nacos 19 20 import ( 21 "bytes" 22 "fmt" 23 "strconv" 24 "strings" 25 "time" 26 ) 27 28 import ( 29 nacosClient "github.com/dubbogo/gost/database/kv/nacos" 30 "github.com/dubbogo/gost/log/logger" 31 32 "github.com/nacos-group/nacos-sdk-go/v2/vo" 33 34 perrors "github.com/pkg/errors" 35 ) 36 37 import ( 38 "dubbo.apache.org/dubbo-go/v3/common" 39 "dubbo.apache.org/dubbo-go/v3/common/constant" 40 "dubbo.apache.org/dubbo-go/v3/common/extension" 41 "dubbo.apache.org/dubbo-go/v3/metrics" 42 metricsRegistry "dubbo.apache.org/dubbo-go/v3/metrics/registry" 43 "dubbo.apache.org/dubbo-go/v3/registry" 44 "dubbo.apache.org/dubbo-go/v3/remoting" 45 "dubbo.apache.org/dubbo-go/v3/remoting/nacos" 46 ) 47 48 const ( 49 RegistryConnDelay = 3 50 ) 51 52 func init() { 53 extension.SetRegistry(constant.NacosKey, newNacosRegistry) 54 } 55 56 type nacosRegistry struct { 57 *common.URL 58 namingClient *nacosClient.NacosNamingClient 59 registryUrls []*common.URL 60 } 61 62 func getCategory(url *common.URL) string { 63 role, _ := strconv.Atoi(url.GetParam(constant.RegistryRoleKey, strconv.Itoa(constant.NacosDefaultRoleType))) 64 category := common.DubboNodes[role] 65 return category 66 } 67 68 func getServiceName(url *common.URL) string { 69 var buffer bytes.Buffer 70 71 buffer.Write([]byte(getCategory(url))) 72 appendParam(&buffer, url, constant.InterfaceKey) 73 appendParam(&buffer, url, constant.VersionKey) 74 appendParam(&buffer, url, constant.GroupKey) 75 return buffer.String() 76 } 77 78 func appendParam(target *bytes.Buffer, url *common.URL, key string) { 79 value := url.GetParam(key, "") 80 target.Write([]byte(constant.NacosServiceNameSeparator)) 81 if strings.TrimSpace(value) != "" { 82 target.Write([]byte(value)) 83 } 84 } 85 86 func createRegisterParam(url *common.URL, serviceName string, groupName string) vo.RegisterInstanceParam { 87 category := getCategory(url) 88 params := make(map[string]string) 89 90 url.RangeParams(func(key, value string) bool { 91 params[key] = value 92 return true 93 }) 94 95 params[constant.NacosCategoryKey] = category 96 params[constant.NacosProtocolKey] = url.Protocol 97 params[constant.NacosPathKey] = url.Path 98 params[constant.MethodsKey] = strings.Join(url.Methods, ",") 99 common.HandleRegisterIPAndPort(url) 100 port, _ := strconv.Atoi(url.Port) 101 instance := vo.RegisterInstanceParam{ 102 Ip: url.Ip, 103 Port: uint64(port), 104 Metadata: params, 105 Weight: 1, 106 Enable: true, 107 Healthy: true, 108 Ephemeral: true, 109 ServiceName: serviceName, 110 GroupName: groupName, 111 } 112 return instance 113 } 114 115 // Register will register the service @url to its nacos registry center. 116 func (nr *nacosRegistry) Register(url *common.URL) error { 117 start := time.Now() 118 serviceName := getServiceName(url) 119 groupName := nr.URL.GetParam(constant.NacosGroupKey, defaultGroup) 120 param := createRegisterParam(url, serviceName, groupName) 121 logger.Infof("[Nacos Registry] Registry instance with param = %+v", param) 122 isRegistry, err := nr.namingClient.Client().RegisterInstance(param) 123 metrics.Publish(metricsRegistry.NewRegisterEvent(err == nil && isRegistry, start)) 124 if err != nil { 125 return err 126 } 127 if !isRegistry { 128 return perrors.New("registry [" + serviceName + "] to nacos failed") 129 } 130 nr.registryUrls = append(nr.registryUrls, url) 131 return nil 132 } 133 134 func createDeregisterParam(url *common.URL, serviceName string, groupName string) vo.DeregisterInstanceParam { 135 common.HandleRegisterIPAndPort(url) 136 port, _ := strconv.Atoi(url.Port) 137 return vo.DeregisterInstanceParam{ 138 Ip: url.Ip, 139 Port: uint64(port), 140 ServiceName: serviceName, 141 GroupName: groupName, 142 Ephemeral: true, 143 } 144 } 145 146 // UnRegister returns nil if unregister successfully. If not, returns an error. 147 func (nr *nacosRegistry) UnRegister(url *common.URL) error { 148 serviceName := getServiceName(url) 149 groupName := nr.URL.GetParam(constant.NacosGroupKey, defaultGroup) 150 param := createDeregisterParam(url, serviceName, groupName) 151 isDeRegistry, err := nr.namingClient.Client().DeregisterInstance(param) 152 if err != nil { 153 return err 154 } 155 if !isDeRegistry { 156 return perrors.New("DeRegistry [" + serviceName + "] to nacos failed") 157 } 158 return nil 159 } 160 161 func (nr *nacosRegistry) subscribe(conf *common.URL) (registry.Listener, error) { 162 return NewNacosListener(conf, nr.URL, nr.namingClient) 163 } 164 165 // Subscribe returns nil if subscribing registry successfully. If not returns an error. 166 func (nr *nacosRegistry) Subscribe(url *common.URL, notifyListener registry.NotifyListener) error { 167 // TODO 168 role, _ := strconv.Atoi(url.GetParam(constant.RegistryRoleKey, "")) 169 if role != common.CONSUMER { 170 return nil 171 } 172 serviceName := getServiceName(url) 173 if serviceName == "*" { 174 // Subscribe to all services 175 for { 176 if !nr.IsAvailable() { 177 logger.Warnf("event listener game over.") 178 return perrors.New("nacosRegistry is not available.") 179 } 180 181 services, err := nr.getAllSubscribeServiceNames() 182 if err != nil { 183 if !nr.IsAvailable() { 184 logger.Warnf("event listener game over.") 185 return err 186 } 187 logger.Warnf("getAllServices() = err:%v", perrors.WithStack(err)) 188 time.Sleep(time.Duration(RegistryConnDelay) * time.Second) 189 continue 190 } 191 192 for _, service := range services { 193 listener, err := nr.subscribeToService(url, service) 194 metrics.Publish(metricsRegistry.NewSubscribeEvent(err == nil)) 195 if err != nil { 196 logger.Warnf("Failed to subscribe to service '%s': %v", service, err) 197 continue 198 } 199 200 nr.handleServiceEvents(listener, notifyListener) 201 } 202 } 203 } else { 204 // Subscribe to a specific service 205 for { 206 if !nr.IsAvailable() { 207 logger.Warnf("event listener game over.") 208 return perrors.New("nacosRegistry is not available.") 209 } 210 211 listener, err := nr.subscribe(url) 212 metrics.Publish(metricsRegistry.NewSubscribeEvent(err == nil)) 213 if err != nil { 214 if !nr.IsAvailable() { 215 logger.Warnf("event listener game over.") 216 return err 217 } 218 logger.Warnf("getListener() = err:%v", perrors.WithStack(err)) 219 time.Sleep(time.Duration(RegistryConnDelay) * time.Second) 220 continue 221 } 222 nr.handleServiceEvents(listener, notifyListener) 223 } 224 } 225 } 226 227 // getAllServices retrieves the list of all services from the registry 228 func (nr *nacosRegistry) getAllSubscribeServiceNames() ([]string, error) { 229 services, err := nr.namingClient.Client().GetAllServicesInfo(vo.GetAllServiceInfoParam{ 230 GroupName: nr.GetParam(constant.RegistryGroupKey, defaultGroup), 231 PageNo: 1, 232 PageSize: 10, 233 }) 234 subScribeServiceNames := []string{} 235 for _, dom := range services.Doms { 236 if strings.HasPrefix(dom, "providers:") { 237 subScribeServiceNames = append(subScribeServiceNames, dom) 238 } 239 } 240 241 return subScribeServiceNames, err 242 } 243 244 // subscribeToService subscribes to a specific service in the registry 245 func (nr *nacosRegistry) subscribeToService(url *common.URL, service string) (listener registry.Listener, err error) { 246 return NewNacosListenerWithServiceName(service, url, nr.URL, nr.namingClient) 247 } 248 249 // handleServiceEvents receives service events from the listener and notifies the notifyListener 250 func (nr *nacosRegistry) handleServiceEvents(listener registry.Listener, notifyListener registry.NotifyListener) { 251 for { 252 serviceEvent, err := listener.Next() 253 if err != nil { 254 logger.Warnf("Selector.watch() = error{%v}", perrors.WithStack(err)) 255 listener.Close() 256 return 257 } 258 logger.Infof("[Nacos Registry] Update begin, service event: %v", serviceEvent.String()) 259 notifyListener.Notify(serviceEvent) 260 } 261 } 262 263 // UnSubscribe : 264 func (nr *nacosRegistry) UnSubscribe(url *common.URL, _ registry.NotifyListener) error { 265 param := createSubscribeParam(url, nr.URL, nil) 266 if param == nil { 267 return nil 268 } 269 err := nr.namingClient.Client().Unsubscribe(param) 270 if err != nil { 271 return perrors.New("UnSubscribe [" + param.ServiceName + "] to nacos failed") 272 } 273 return nil 274 } 275 276 // LoadSubscribeInstances load subscribe instance 277 func (nr *nacosRegistry) LoadSubscribeInstances(url *common.URL, notify registry.NotifyListener) error { 278 serviceName := getSubscribeName(url) 279 groupName := nr.GetURL().GetParam(constant.RegistryGroupKey, defaultGroup) 280 instances, err := nr.namingClient.Client().SelectAllInstances(vo.SelectAllInstancesParam{ 281 ServiceName: serviceName, 282 GroupName: groupName, 283 }) 284 if err != nil { 285 return perrors.New(fmt.Sprintf("could not query the instances for serviceName=%s,groupName=%s,error=%v", 286 serviceName, groupName, err)) 287 } 288 289 for i := range instances { 290 if newUrl := generateUrl(instances[i]); newUrl != nil { 291 notify.Notify(®istry.ServiceEvent{Action: remoting.EventTypeAdd, Service: newUrl}) 292 } 293 } 294 return nil 295 } 296 297 func createSubscribeParam(url, regUrl *common.URL, cb callback) *vo.SubscribeParam { 298 serviceName := getSubscribeName(url) 299 groupName := regUrl.GetParam(constant.RegistryGroupKey, defaultGroup) 300 if cb == nil { 301 v, ok := listenerCache.Load(serviceName + groupName) 302 if !ok { 303 return nil 304 } 305 listener, ok := v.(*nacosListener) 306 if !ok { 307 return nil 308 } 309 cb = listener.Callback 310 } 311 return &vo.SubscribeParam{ 312 ServiceName: serviceName, 313 SubscribeCallback: cb, 314 GroupName: groupName, 315 } 316 } 317 318 func createSubscribeParamWithServiceName(serviceName string, regUrl *common.URL, cb callback) *vo.SubscribeParam { 319 groupName := regUrl.GetParam(constant.RegistryGroupKey, defaultGroup) 320 if cb == nil { 321 v, ok := listenerCache.Load(serviceName + groupName) 322 if !ok { 323 return nil 324 } 325 listener, ok := v.(*nacosListener) 326 if !ok { 327 return nil 328 } 329 cb = listener.Callback 330 } 331 return &vo.SubscribeParam{ 332 ServiceName: serviceName, 333 SubscribeCallback: cb, 334 GroupName: groupName, 335 } 336 } 337 338 // GetURL gets its registration URL 339 func (nr *nacosRegistry) GetURL() *common.URL { 340 return nr.URL 341 } 342 343 // IsAvailable determines nacos registry center whether it is available 344 func (nr *nacosRegistry) IsAvailable() bool { 345 // TODO 346 return true 347 } 348 349 // nolint 350 func (nr *nacosRegistry) Destroy() { 351 for _, url := range nr.registryUrls { 352 err := nr.UnRegister(url) 353 logger.Infof("DeRegister Nacos URL:%+v", url) 354 if err != nil { 355 logger.Errorf("Deregister URL:%+v err:%v", url, err.Error()) 356 } 357 } 358 return 359 } 360 361 // newNacosRegistry will create new instance 362 func newNacosRegistry(url *common.URL) (registry.Registry, error) { 363 logger.Infof("[Nacos Registry] New nacos registry with url = %+v", url.ToMap()) 364 // key transfer: registry -> nacos 365 url.SetParam(constant.NacosNamespaceID, url.GetParam(constant.RegistryNamespaceKey, "")) 366 url.SetParam(constant.NacosUsername, url.Username) 367 url.SetParam(constant.NacosPassword, url.Password) 368 url.SetParam(constant.NacosAccessKey, url.GetParam(constant.RegistryAccessKey, "")) 369 url.SetParam(constant.NacosSecretKey, url.GetParam(constant.RegistrySecretKey, "")) 370 url.SetParam(constant.NacosTimeout, url.GetParam(constant.RegistryTimeoutKey, constant.DefaultRegTimeout)) 371 url.SetParam(constant.NacosGroupKey, url.GetParam(constant.RegistryGroupKey, defaultGroup)) 372 namingClient, err := nacos.NewNacosClientByURL(url) 373 if err != nil { 374 return &nacosRegistry{}, err 375 } 376 tmpRegistry := &nacosRegistry{ 377 URL: url, // registry.group is recorded at this url 378 namingClient: namingClient, 379 registryUrls: []*common.URL{}, 380 } 381 return tmpRegistry, nil 382 }