dubbo.apache.org/dubbo-go/v3@v3.1.1/registry/base_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 registry 19 20 import ( 21 "fmt" 22 "net/url" 23 "os" 24 "strconv" 25 "strings" 26 "sync" 27 "time" 28 ) 29 30 import ( 31 "github.com/dubbogo/gost/log/logger" 32 33 perrors "github.com/pkg/errors" 34 ) 35 36 import ( 37 "dubbo.apache.org/dubbo-go/v3/common" 38 "dubbo.apache.org/dubbo-go/v3/common/constant" 39 "dubbo.apache.org/dubbo-go/v3/metrics" 40 metricsRegistry "dubbo.apache.org/dubbo-go/v3/metrics/registry" 41 ) 42 43 const ( 44 RegistryConnDelay = 3 // connection delay 45 MaxWaitInterval = 3 * time.Second // max wait interval 46 ) 47 48 var ( 49 processID = "" 50 localIP = "" 51 ) 52 53 func init() { 54 processID = fmt.Sprintf("%d", os.Getpid()) 55 localIP = common.GetLocalIp() 56 } 57 58 type createPathFunc func(dubboPath string) error 59 60 // FacadeBasedRegistry is the interface of Registry, and it is designed for registry who 61 // want to inherit BaseRegistry. If there is no special case, you'd better inherit BaseRegistry 62 // and implement the FacadeBasedRegistry interface instead of directly implementing the 63 // Registry interface. 64 // 65 // CreatePath method create the path in the registry. 66 // 67 // DoRegister method actually does the register job. 68 // 69 // DoUnregister method does the unregister job. 70 // 71 // DoSubscribe method actually subscribes the URL. 72 // 73 // DoUnsubscribe method does unsubscribe the URL. 74 // 75 // CloseAndNilClient method closes the client and then reset the client in registry to nil 76 // you should notice that this method will be invoked inside a lock. 77 // So you should implement this method as light weighted as you can. 78 // 79 // CloseListener method closes listeners. 80 // 81 // InitListeners method init listeners 82 type FacadeBasedRegistry interface { 83 Registry 84 85 CreatePath(string) error 86 DoRegister(string, string) error 87 DoUnregister(string, string) error 88 DoSubscribe(conf *common.URL) (Listener, error) 89 DoUnsubscribe(conf *common.URL) (Listener, error) 90 CloseAndNilClient() 91 CloseListener() 92 InitListeners() 93 } 94 95 // BaseRegistry is a common logic abstract for registry. It implement Registry interface. 96 type BaseRegistry struct { 97 facadeBasedRegistry FacadeBasedRegistry 98 *common.URL 99 birth int64 // time of file birth, seconds since Epoch; 0 if unknown 100 wg sync.WaitGroup // wg+done for zk restart 101 done chan struct{} 102 registered *sync.Map 103 cltLock sync.RWMutex 104 } 105 106 // InitBaseRegistry for init some local variables and set BaseRegistry's subclass to it 107 func (r *BaseRegistry) InitBaseRegistry(url *common.URL, facadeRegistry FacadeBasedRegistry) Registry { 108 r.URL = url 109 r.birth = time.Now().UnixNano() 110 r.done = make(chan struct{}) 111 r.registered = &sync.Map{} 112 r.facadeBasedRegistry = facadeRegistry 113 return r 114 } 115 116 // GetURL for get registry's url 117 func (r *BaseRegistry) GetURL() *common.URL { 118 return r.URL 119 } 120 121 // Destroy for graceful down 122 func (r *BaseRegistry) Destroy() { 123 // first step close registry's all listeners 124 r.facadeBasedRegistry.CloseListener() 125 // then close r.done to notify other program who listen to it 126 close(r.Done()) 127 // wait waitgroup done (wait listeners outside close over) 128 r.WaitGroup().Wait() 129 130 // close registry client 131 r.closeRegisters() 132 } 133 134 // Register implement interface registry to register 135 func (r *BaseRegistry) Register(url *common.URL) error { 136 start := time.Now() 137 // todo bug when provider、consumer simultaneous initialization 138 if _, ok := r.registered.Load(url.Key()); ok { 139 return perrors.Errorf("Service {%s} has been registered", url.Key()) 140 } 141 142 err := r.register(url) 143 defer metrics.Publish(metricsRegistry.NewRegisterEvent(err == nil, start)) 144 if err == nil { 145 r.registered.Store(url.Key(), url) 146 147 } else { 148 err = perrors.WithMessagef(err, "register(url:%+v)", url) 149 } 150 151 return err 152 } 153 154 // UnRegister implement interface registry to unregister 155 func (r *BaseRegistry) UnRegister(url *common.URL) error { 156 if _, ok := r.registered.Load(url.Key()); !ok { 157 return perrors.Errorf("Service {%s} has not registered", url.Key()) 158 } 159 err := r.unregister(url) 160 metrics.Publish(metricsRegistry.NewSubscribeEvent(err == nil)) 161 if err == nil { 162 r.registered.Delete(url.Key()) 163 } else { 164 err = perrors.WithMessagef(err, "unregister(url:%+v)", url) 165 } 166 167 return err 168 } 169 170 // service is for getting service path stored in url 171 func (r *BaseRegistry) service(c *common.URL) string { 172 return url.QueryEscape(c.Service()) 173 } 174 175 // RestartCallBack for reregister when reconnect 176 func (r *BaseRegistry) RestartCallBack() bool { 177 flag := true 178 r.registered.Range(func(key, value interface{}) bool { 179 registeredUrl := value.(*common.URL) 180 err := r.register(registeredUrl) 181 if err != nil { 182 flag = false 183 logger.Errorf("failed to re-register service :%v, error{%#v}", 184 registeredUrl, perrors.WithStack(err)) 185 return flag 186 } 187 188 logger.Infof("success to re-register service :%v", registeredUrl.Key()) 189 return flag 190 }) 191 192 if flag { 193 r.facadeBasedRegistry.InitListeners() 194 } 195 196 return flag 197 } 198 199 // register for register url to registry, include init params 200 func (r *BaseRegistry) register(c *common.URL) error { 201 return r.processURL(c, r.facadeBasedRegistry.DoRegister, r.createPath) 202 } 203 204 // unregister for unregister url to registry, include init params 205 func (r *BaseRegistry) unregister(c *common.URL) error { 206 return r.processURL(c, r.facadeBasedRegistry.DoUnregister, nil) 207 } 208 209 func (r *BaseRegistry) processURL(c *common.URL, f func(string, string) error, cpf createPathFunc) error { 210 if f == nil { 211 panic(" Must provide a `function(string, string) error` to process URL. ") 212 } 213 var ( 214 err error 215 params url.Values 216 rawURL string 217 encodedURL string 218 dubboPath string 219 ) 220 params = url.Values{} 221 222 c.RangeParams(func(key, value string) bool { 223 params.Add(key, value) 224 return true 225 }) 226 227 role, _ := strconv.Atoi(c.GetParam(constant.RegistryRoleKey, "")) 228 switch role { 229 case common.PROVIDER: 230 dubboPath, rawURL, err = r.providerRegistry(c, params, cpf) 231 case common.CONSUMER: 232 dubboPath, rawURL, err = r.consumerRegistry(c, params, cpf) 233 default: 234 return perrors.Errorf("@c{%v} type is not referencer or provider", c) 235 } 236 if err != nil { 237 return perrors.WithMessagef(err, "@c{%v} registry fail", c) 238 } 239 240 encodedURL = url.QueryEscape(rawURL) 241 dubboPath = strings.ReplaceAll(dubboPath, "$", "%24") 242 err = f(dubboPath, encodedURL) 243 244 if err != nil { 245 return perrors.WithMessagef(err, "register Node(path:%s, url:%s)", dubboPath, rawURL) 246 } 247 return nil 248 } 249 250 // createPath will create dubbo path in register 251 func (r *BaseRegistry) createPath(dubboPath string) error { 252 r.cltLock.Lock() 253 defer r.cltLock.Unlock() 254 return r.facadeBasedRegistry.CreatePath(dubboPath) 255 } 256 257 // providerRegistry for provider role do 258 func (r *BaseRegistry) providerRegistry(c *common.URL, params url.Values, f createPathFunc) (string, string, error) { 259 var ( 260 dubboPath string 261 rawURL string 262 err error 263 ) 264 if c.Path == "" || len(c.Methods) == 0 { 265 return "", "", perrors.Errorf("conf{Path:%s, Methods:%s}", c.Path, c.Methods) 266 } 267 dubboPath = fmt.Sprintf("/%s/%s/%s", r.URL.GetParam(constant.RegistryGroupKey, "dubbo"), r.service(c), common.DubboNodes[common.PROVIDER]) 268 if f != nil { 269 err = f(dubboPath) 270 } 271 if err != nil { 272 logger.Errorf("facadeBasedRegistry.CreatePath(path{%s}) = error{%#v}", dubboPath, perrors.WithStack(err)) 273 return "", "", perrors.WithMessagef(err, "facadeBasedRegistry.CreatePath(path:%s)", dubboPath) 274 } 275 params.Add(constant.AnyhostKey, "true") 276 277 // Dubbo java consumer to start looking for the provider url,because the category does not match, 278 // the provider will not find, causing the consumer can not start, so we use consumers. 279 280 if len(c.Methods) != 0 { 281 params.Add(constant.MethodsKey, strings.Join(c.Methods, ",")) 282 } 283 logger.Debugf("provider url params:%#v", params) 284 var host string 285 if len(c.Ip) == 0 { 286 host = localIP 287 } else { 288 host = c.Ip 289 } 290 host += ":" + c.Port 291 292 // delete empty param key 293 for key, val := range params { 294 if len(val) > 0 && val[0] == "" { 295 params.Del(key) 296 } 297 } 298 299 s, _ := url.QueryUnescape(params.Encode()) 300 rawURL = fmt.Sprintf("%s://%s%s?%s", c.Protocol, host, c.Path, s) 301 // Print your own registration service providers. 302 logger.Debugf("provider path:%s, url:%s", dubboPath, rawURL) 303 return dubboPath, rawURL, nil 304 } 305 306 // consumerRegistry for consumer role do 307 func (r *BaseRegistry) consumerRegistry(c *common.URL, params url.Values, f createPathFunc) (string, string, error) { 308 var ( 309 dubboPath string 310 rawURL string 311 err error 312 ) 313 dubboPath = fmt.Sprintf("/%s/%s/%s", r.URL.GetParam(constant.RegistryGroupKey, "dubbo"), r.service(c), common.DubboNodes[common.CONSUMER]) 314 315 if f != nil { 316 err = f(dubboPath) 317 } 318 319 if err != nil { 320 logger.Errorf("facadeBasedRegistry.CreatePath(path{%s}) = error{%v}", dubboPath, perrors.WithStack(err)) 321 return "", "", perrors.WithStack(err) 322 } 323 324 params.Add("protocol", c.Protocol) 325 s, _ := url.QueryUnescape(params.Encode()) 326 rawURL = fmt.Sprintf("consumer://%s%s?%s", localIP, c.Path, s) 327 logger.Debugf("consumer path:%s, url:%s", dubboPath, rawURL) 328 return dubboPath, rawURL, nil 329 } 330 331 // sleepWait... 332 func sleepWait(n int) { 333 wait := time.Duration((n + 1) * 2e8) 334 if wait > MaxWaitInterval { 335 wait = MaxWaitInterval 336 } 337 time.Sleep(wait) 338 } 339 340 // Subscribe :subscribe from registry, event will notify by notifyListener 341 func (r *BaseRegistry) Subscribe(url *common.URL, notifyListener NotifyListener) error { 342 for { 343 if !r.IsAvailable() { 344 logger.Warnf("event listener game over.") 345 return perrors.New("BaseRegistry is not available.") 346 } 347 348 listener, err := r.facadeBasedRegistry.DoSubscribe(url) 349 if err != nil { 350 if !r.IsAvailable() { 351 logger.Warnf("event listener game over.") 352 return err 353 } 354 logger.Warnf("getListener() = err:%v", perrors.WithStack(err)) 355 time.Sleep(time.Duration(RegistryConnDelay) * time.Second) 356 continue 357 } 358 359 for { 360 if serviceEvent, err := listener.Next(); err != nil { 361 logger.Warnf("Selector.watch() = error{%v}", perrors.WithStack(err)) 362 listener.Close() 363 return nil 364 } else { 365 logger.Debugf("[Registry] update begin, service event: %v", serviceEvent.String()) 366 notifyListener.Notify(serviceEvent) 367 } 368 } 369 } 370 } 371 372 // UnSubscribe URL 373 func (r *BaseRegistry) UnSubscribe(url *common.URL, notifyListener NotifyListener) error { 374 if !r.IsAvailable() { 375 logger.Warnf("event listener game over.") 376 return perrors.New("BaseRegistry is not available.") 377 } 378 379 listener, err := r.facadeBasedRegistry.DoUnsubscribe(url) 380 if err != nil { 381 if !r.IsAvailable() { 382 logger.Warnf("event listener game over.") 383 return perrors.New("BaseRegistry is not available.") 384 } 385 logger.Warnf("getListener() = err:%v", perrors.WithStack(err)) 386 return perrors.WithStack(err) 387 } 388 389 if listener != nil { 390 listener.Close() 391 } 392 393 return nil 394 } 395 396 // LoadSubscribeInstances load subscribe instance 397 func (r *BaseRegistry) LoadSubscribeInstances(url *common.URL, notify NotifyListener) error { 398 return r.facadeBasedRegistry.LoadSubscribeInstances(url, notify) 399 } 400 401 // closeRegisters close and remove registry client and reset services map 402 func (r *BaseRegistry) closeRegisters() { 403 logger.Infof("begin to close provider client") 404 r.cltLock.Lock() 405 defer r.cltLock.Unlock() 406 // Close and remove(set to nil) the registry client 407 r.facadeBasedRegistry.CloseAndNilClient() 408 // reset the services map 409 r.registered = nil 410 } 411 412 // IsAvailable judge to is registry not closed by chan r.done 413 func (r *BaseRegistry) IsAvailable() bool { 414 select { 415 case <-r.Done(): 416 return false 417 default: 418 return true 419 } 420 } 421 422 // WaitGroup open for outside add the waitgroup to add some logic before registry destroyed over(graceful down) 423 func (r *BaseRegistry) WaitGroup() *sync.WaitGroup { 424 return &r.wg 425 } 426 427 // Done open for outside to listen the event of registry Destroy() called. 428 func (r *BaseRegistry) Done() chan struct{} { 429 return r.done 430 }