dubbo.apache.org/dubbo-go/v3@v3.1.1/registry/zookeeper/listener.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 "strings" 22 "sync" 23 ) 24 25 import ( 26 gxchan "github.com/dubbogo/gost/container/chan" 27 gxzookeeper "github.com/dubbogo/gost/database/kv/zk" 28 "github.com/dubbogo/gost/log/logger" 29 30 perrors "github.com/pkg/errors" 31 ) 32 33 import ( 34 "dubbo.apache.org/dubbo-go/v3/common" 35 "dubbo.apache.org/dubbo-go/v3/common/constant" 36 "dubbo.apache.org/dubbo-go/v3/config_center" 37 "dubbo.apache.org/dubbo-go/v3/registry" 38 "dubbo.apache.org/dubbo-go/v3/remoting" 39 ) 40 41 // RegistryDataListener contains all URL information subscribed by zookeeper registry 42 type RegistryDataListener struct { 43 subscribed map[string]config_center.ConfigurationListener 44 mutex sync.Mutex 45 closed bool 46 } 47 48 // NewRegistryDataListener constructs a new RegistryDataListener 49 func NewRegistryDataListener() *RegistryDataListener { 50 return &RegistryDataListener{ 51 subscribed: make(map[string]config_center.ConfigurationListener), 52 } 53 } 54 55 // SubscribeURL is used to set a watch listener for url 56 func (l *RegistryDataListener) SubscribeURL(url *common.URL, listener config_center.ConfigurationListener) { 57 if l.closed { 58 return 59 } 60 l.subscribed[url.ServiceKey()] = listener 61 } 62 63 // UnSubscribeURL is used to unset a watch listener for url 64 func (l *RegistryDataListener) UnSubscribeURL(url *common.URL) config_center.ConfigurationListener { 65 if l.closed { 66 return nil 67 } 68 listener := l.subscribed[url.ServiceKey()] 69 listener.(*RegistryConfigurationListener).Close() 70 delete(l.subscribed, url.ServiceKey()) 71 return listener 72 } 73 74 // DataChange accepts all events sent from the zookeeper server and trigger the corresponding listener for processing 75 func (l *RegistryDataListener) DataChange(event remoting.Event) bool { 76 providersPath := constant.PathSeparator + constant.ProviderCategory + constant.PathSeparator 77 // Intercept the last bit 78 index := strings.Index(event.Path, providersPath) 79 if index == -1 { 80 logger.Warnf("[RegistryDataListener][DataChange]Listen error zk node path {%s}, "+ 81 "this listener is used to listen services which under the directory of providers/", event.Path) 82 return false 83 } 84 url := event.Path[index+len(providersPath):] 85 serviceURL, err := common.NewURL(url) 86 if err != nil { 87 logger.Errorf("[RegistryDataListener][DataChange]Listen NewURL({%s}) = error{%+v} event.Path={%s}", url, err, event.Path) 88 return false 89 } 90 l.mutex.Lock() 91 defer l.mutex.Unlock() 92 if l.closed { 93 return false 94 } 95 match := false 96 for serviceKey, listener := range l.subscribed { 97 intf, group, version := common.ParseServiceKey(serviceKey) 98 if serviceURL.ServiceKey() == serviceKey || common.IsAnyCondition(intf, group, version, serviceURL) { 99 listener.Process( 100 &config_center.ConfigChangeEvent{ 101 Key: event.Path, 102 Value: serviceURL.Clone(), 103 ConfigType: event.Action, 104 }, 105 ) 106 match = true 107 } 108 } 109 return match 110 } 111 112 // Close all RegistryConfigurationListener in subscribed 113 func (l *RegistryDataListener) Close() { 114 l.mutex.Lock() 115 defer l.mutex.Unlock() 116 l.closed = true 117 for _, listener := range l.subscribed { 118 listener.(*RegistryConfigurationListener).Close() 119 } 120 } 121 122 // RegistryConfigurationListener represent the processor of zookeeper watcher 123 type RegistryConfigurationListener struct { 124 client *gxzookeeper.ZookeeperClient 125 registry *zkRegistry 126 events *gxchan.UnboundedChan 127 isClosed bool 128 close chan struct{} 129 closeOnce sync.Once 130 subscribeURL *common.URL 131 } 132 133 // NewRegistryConfigurationListener for listening the event of zk. 134 func NewRegistryConfigurationListener(client *gxzookeeper.ZookeeperClient, reg *zkRegistry, conf *common.URL) *RegistryConfigurationListener { 135 reg.WaitGroup().Add(1) 136 return &RegistryConfigurationListener{ 137 client: client, 138 registry: reg, 139 events: gxchan.NewUnboundedChan(32), 140 isClosed: false, 141 close: make(chan struct{}, 1), 142 subscribeURL: conf, 143 } 144 } 145 146 // Process submit the ConfigChangeEvent to the event chan to notify all observer 147 func (l *RegistryConfigurationListener) Process(configType *config_center.ConfigChangeEvent) { 148 l.events.In() <- configType 149 } 150 151 // Next will observe the registry state and events chan 152 func (l *RegistryConfigurationListener) Next() (*registry.ServiceEvent, error) { 153 for { 154 select { 155 case <-l.close: 156 return nil, perrors.New("listener has been closed") 157 case <-l.registry.Done(): 158 logger.Warnf("zk consumer register has quit, so zk event listener exit now. (registry url {%v}", l.registry.BaseRegistry.URL) 159 return nil, perrors.New("zookeeper registry, (registry url{%v}) stopped") 160 case val := <-l.events.Out(): 161 e, _ := val.(*config_center.ConfigChangeEvent) 162 logger.Debugf("got zk event %s", e) 163 if e.ConfigType == remoting.EventTypeDel && !l.valid() { 164 logger.Warnf("update @result{%s}. But its connection to registry is invalid", e.Value) 165 continue 166 } 167 return ®istry.ServiceEvent{Action: e.ConfigType, Service: e.Value.(*common.URL)}, nil 168 } 169 } 170 } 171 172 // Close RegistryConfigurationListener only once 173 func (l *RegistryConfigurationListener) Close() { 174 // ensure that the listener will be closed at most once. 175 l.closeOnce.Do(func() { 176 l.isClosed = true 177 l.close <- struct{}{} 178 l.registry.WaitGroup().Done() 179 }) 180 } 181 182 // valid return the true if the client conn isn't nil 183 func (l *RegistryConfigurationListener) valid() bool { 184 return l.client.ZkConnValid() 185 }