github.com/dubbogo/gost@v1.14.0/database/kv/zk/client.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 gxzookeeper 19 20 import ( 21 "path" 22 "strings" 23 "sync" 24 "sync/atomic" 25 "time" 26 ) 27 28 import ( 29 "github.com/dubbogo/go-zookeeper/zk" 30 31 perrors "github.com/pkg/errors" 32 ) 33 34 const ( 35 SLASH = "/" 36 ) 37 38 var ( 39 zkClientPool zookeeperClientPool 40 clientPoolOnce sync.Once 41 42 // ErrNilZkClientConn no conn error 43 ErrNilZkClientConn = perrors.New("Zookeeper Client{conn} is nil") 44 ErrStatIsNil = perrors.New("Stat of the node is nil") 45 ) 46 47 // ZookeeperClient represents zookeeper Client Configuration 48 type ZookeeperClient struct { 49 name string 50 ZkAddrs []string 51 sync.RWMutex // for conn 52 Conn *zk.Conn 53 activeNumber uint32 54 Timeout time.Duration 55 Wait sync.WaitGroup 56 valid uint32 57 share bool 58 initialized uint32 59 reconnectCh chan struct{} 60 eventRegistry map[string][]chan zk.Event 61 eventRegistryLock sync.RWMutex 62 zkEventHandler ZkEventHandler 63 Session <-chan zk.Event 64 } 65 66 type zookeeperClientPool struct { 67 sync.Mutex 68 zkClient map[string]*ZookeeperClient 69 } 70 71 // ZkEventHandler interface 72 type ZkEventHandler interface { 73 HandleZkEvent(z *ZookeeperClient) 74 } 75 76 // DefaultHandler is default handler for zk event 77 type DefaultHandler struct{} 78 79 // StateToString will transfer zk state to string 80 func StateToString(state zk.State) string { 81 switch state { 82 case zk.StateDisconnected: 83 return "zookeeper disconnected" 84 case zk.StateConnecting: 85 return "zookeeper connecting" 86 case zk.StateAuthFailed: 87 return "zookeeper auth failed" 88 case zk.StateConnectedReadOnly: 89 return "zookeeper connect readonly" 90 case zk.StateSaslAuthenticated: 91 return "zookeeper sasl authenticated" 92 case zk.StateExpired: 93 return "zookeeper connection expired" 94 case zk.StateConnected: 95 return "zookeeper connected" 96 case zk.StateHasSession: 97 return "zookeeper has Session" 98 case zk.StateUnknown: 99 return "zookeeper unknown state" 100 default: 101 return state.String() 102 } 103 } 104 105 func initZookeeperClientPool() { 106 zkClientPool.zkClient = make(map[string]*ZookeeperClient) 107 } 108 109 // NewZookeeperClient will create a ZookeeperClient 110 func NewZookeeperClient(name string, zkAddrs []string, share bool, opts ...zkClientOption) (*ZookeeperClient, error) { 111 if !share { 112 return newClient(name, zkAddrs, share, opts...) 113 } 114 clientPoolOnce.Do(initZookeeperClientPool) 115 zkClientPool.Lock() 116 defer zkClientPool.Unlock() 117 if zkClient, ok := zkClientPool.zkClient[name]; ok { 118 zkClient.activeNumber++ 119 return zkClient, nil 120 } 121 newZkClient, err := newClient(name, zkAddrs, share, opts...) 122 if err != nil { 123 return nil, err 124 } 125 zkClientPool.zkClient[name] = newZkClient 126 return newZkClient, nil 127 } 128 129 func newClient(name string, zkAddrs []string, share bool, opts ...zkClientOption) (*ZookeeperClient, error) { 130 newZkClient := &ZookeeperClient{ 131 name: name, 132 ZkAddrs: zkAddrs, 133 activeNumber: 0, 134 share: share, 135 reconnectCh: make(chan struct{}), 136 eventRegistry: make(map[string][]chan zk.Event), 137 Session: make(<-chan zk.Event), 138 zkEventHandler: &DefaultHandler{}, 139 } 140 for _, opt := range opts { 141 opt(newZkClient) 142 } 143 err := newZkClient.createZookeeperConn() 144 if err != nil { 145 return nil, err 146 } 147 newZkClient.activeNumber++ 148 return newZkClient, nil 149 } 150 151 // nolint 152 func (z *ZookeeperClient) createZookeeperConn() error { 153 var err error 154 155 // connect to zookeeper 156 z.Conn, z.Session, err = zk.Connect(z.ZkAddrs, z.Timeout) 157 if err != nil { 158 return err 159 } 160 atomic.StoreUint32(&z.valid, 1) 161 go z.zkEventHandler.HandleZkEvent(z) 162 return nil 163 } 164 165 // WithTestCluster sets test cluster for zk Client 166 func WithTestCluster(ts *zk.TestCluster) Option { 167 return func(opt *options) { 168 opt.Ts = ts 169 } 170 } 171 172 // NewMockZookeeperClient returns a mock Client instance 173 func NewMockZookeeperClient(name string, timeout time.Duration, opts ...Option) (*zk.TestCluster, *ZookeeperClient, <-chan zk.Event, error) { 174 var ( 175 err error 176 z *ZookeeperClient 177 ts *zk.TestCluster 178 ) 179 180 z = &ZookeeperClient{ 181 name: name, 182 ZkAddrs: []string{}, 183 Timeout: timeout, 184 share: false, 185 reconnectCh: make(chan struct{}), 186 eventRegistry: make(map[string][]chan zk.Event), 187 Session: make(<-chan zk.Event), 188 zkEventHandler: &DefaultHandler{}, 189 } 190 191 option := &options{} 192 for _, opt := range opts { 193 opt(option) 194 } 195 196 // connect to zookeeper 197 if option.Ts != nil { 198 ts = option.Ts 199 } else { 200 ts, err = zk.StartTestCluster(1, nil, nil, zk.WithRetryTimes(40)) 201 if err != nil { 202 return nil, nil, nil, perrors.WithMessagef(err, "zk.StartTestCluster fail") 203 } 204 } 205 206 z.Conn, z.Session, err = ts.ConnectWithOptions(timeout) 207 if err != nil { 208 return nil, nil, nil, perrors.WithMessagef(err, "zk.Connect fail") 209 } 210 atomic.StoreUint32(&z.valid, 1) 211 z.activeNumber++ 212 return ts, z, z.Session, nil 213 } 214 215 // HandleZkEvent handles zookeeper events 216 func (d *DefaultHandler) HandleZkEvent(z *ZookeeperClient) { 217 var ( 218 ok bool 219 state int 220 event zk.Event 221 ) 222 for { 223 select { 224 case event, ok = <-z.Session: 225 if !ok { 226 // channel already closed 227 return 228 } 229 switch event.State { 230 case zk.StateDisconnected: 231 atomic.StoreUint32(&z.valid, 0) 232 case zk.StateConnected: 233 z.eventRegistryLock.RLock() 234 for path, a := range z.eventRegistry { 235 if strings.HasPrefix(event.Path, path) { 236 for _, e := range a { 237 e <- event 238 } 239 } 240 } 241 z.eventRegistryLock.RUnlock() 242 case zk.StateConnecting, zk.StateHasSession: 243 if state == (int)(zk.StateHasSession) { 244 continue 245 } 246 if event.State == zk.StateHasSession { 247 atomic.StoreUint32(&z.valid, 1) 248 //if this is the first connection, don't trigger reconnect event 249 if !atomic.CompareAndSwapUint32(&z.initialized, 0, 1) { 250 close(z.reconnectCh) 251 z.reconnectCh = make(chan struct{}) 252 } 253 } 254 z.eventRegistryLock.RLock() 255 if a, ok := z.eventRegistry[event.Path]; ok && 0 < len(a) { 256 for _, e := range a { 257 e <- event 258 } 259 } 260 z.eventRegistryLock.RUnlock() 261 } 262 state = (int)(event.State) 263 } 264 } 265 } 266 267 // RegisterEvent registers zookeeper events 268 func (z *ZookeeperClient) RegisterEvent(zkPath string, event chan zk.Event) { 269 if zkPath == "" { 270 return 271 } 272 273 z.eventRegistryLock.Lock() 274 defer z.eventRegistryLock.Unlock() 275 a := z.eventRegistry[zkPath] 276 a = append(a, event) 277 z.eventRegistry[zkPath] = a 278 } 279 280 // UnregisterEvent unregisters zookeeper events 281 func (z *ZookeeperClient) UnregisterEvent(zkPath string, event chan zk.Event) { 282 if zkPath == "" { 283 return 284 } 285 286 z.eventRegistryLock.Lock() 287 defer z.eventRegistryLock.Unlock() 288 infoList, ok := z.eventRegistry[zkPath] 289 if !ok { 290 return 291 } 292 for i, e := range infoList { 293 if e == event { 294 infoList = append(infoList[:i], infoList[i+1:]...) 295 } 296 } 297 if len(infoList) == 0 { 298 delete(z.eventRegistry, zkPath) 299 } else { 300 z.eventRegistry[zkPath] = infoList 301 } 302 } 303 304 // ZkConnValid validates zookeeper connection 305 func (z *ZookeeperClient) ZkConnValid() bool { 306 if atomic.LoadUint32(&z.valid) == 1 { 307 return true 308 } 309 return false 310 } 311 312 // Create will create the node recursively, which means that if the parent node is absent, 313 // it will create parent node first. 314 // And the value for the basePath is "" 315 func (z *ZookeeperClient) Create(basePath string) error { 316 return z.CreateWithValue(basePath, []byte{}) 317 } 318 319 // CreateWithValue will create the node recursively, which means that if the parent node is absent, 320 // it will create parent node first. 321 // basePath should start with "/" 322 func (z *ZookeeperClient) CreateWithValue(basePath string, value []byte) error { 323 conn := z.getConn() 324 if conn == nil { 325 return ErrNilZkClientConn 326 } 327 328 if !strings.HasPrefix(basePath, SLASH) { 329 basePath = SLASH + basePath 330 } 331 paths := strings.Split(basePath, SLASH) 332 // Check the ancestor's path 333 for idx := 2; idx < len(paths); idx++ { 334 tmpPath := strings.Join(paths[:idx], SLASH) 335 _, err := conn.Create(tmpPath, []byte{}, 0, zk.WorldACL(zk.PermAll)) 336 if err != nil && err != zk.ErrNodeExists { 337 return perrors.WithMessagef(err, "Error while invoking zk.Create(path:%s), the reason maybe is: ", tmpPath) 338 } 339 } 340 341 _, err := conn.Create(basePath, value, 0, zk.WorldACL(zk.PermAll)) 342 if err != nil { 343 return err 344 } 345 return nil 346 } 347 348 // CreateTempWithValue will create the node recursively, which means that if the parent node is absent, 349 // it will create parent node first,and set value in last child path 350 // If the path exist, it will update data 351 func (z *ZookeeperClient) CreateTempWithValue(basePath string, value []byte) error { 352 var ( 353 err error 354 tmpPath string 355 ) 356 357 conn := z.getConn() 358 if conn == nil { 359 return ErrNilZkClientConn 360 } 361 362 if !strings.HasPrefix(basePath, SLASH) { 363 basePath = SLASH + basePath 364 } 365 pathSlice := strings.Split(basePath, SLASH)[1:] 366 length := len(pathSlice) 367 for i, str := range pathSlice { 368 tmpPath = path.Join(tmpPath, SLASH, str) 369 // last child need be ephemeral 370 if i == length-1 { 371 _, err = conn.Create(tmpPath, value, zk.FlagEphemeral, zk.WorldACL(zk.PermAll)) 372 if err != nil { 373 return perrors.WithMessagef(err, "Error while invoking zk.Create(path:%s), the reason maybe is: ", tmpPath) 374 } 375 break 376 } 377 // we need ignore node exists error for those parent node 378 _, err = conn.Create(tmpPath, []byte{}, 0, zk.WorldACL(zk.PermAll)) 379 if err != nil && err != zk.ErrNodeExists { 380 return perrors.WithMessagef(err, "Error while invoking zk.Create(path:%s), the reason maybe is: ", tmpPath) 381 } 382 } 383 384 return nil 385 } 386 387 // Delete will delete basePath 388 func (z *ZookeeperClient) Delete(basePath string) error { 389 conn := z.getConn() 390 if conn == nil { 391 return ErrNilZkClientConn 392 } 393 return perrors.WithMessagef(conn.Delete(basePath, -1), "Delete(basePath:%s)", basePath) 394 } 395 396 // RegisterTemp registers temporary node by @basePath and @node 397 func (z *ZookeeperClient) RegisterTemp(basePath string, node string) (string, error) { 398 zkPath := path.Join(basePath) + SLASH + node 399 conn := z.getConn() 400 if conn == nil { 401 return "", ErrNilZkClientConn 402 } 403 tmpPath, err := conn.Create(zkPath, []byte(""), zk.FlagEphemeral, zk.WorldACL(zk.PermAll)) 404 405 if err != nil { 406 return zkPath, perrors.WithStack(err) 407 } 408 409 return tmpPath, nil 410 } 411 412 // RegisterTempSeq register temporary sequence node by @basePath and @data 413 func (z *ZookeeperClient) RegisterTempSeq(basePath string, data []byte) (string, error) { 414 var ( 415 err error 416 tmpPath string 417 ) 418 419 err = ErrNilZkClientConn 420 conn := z.getConn() 421 if conn != nil { 422 tmpPath, err = conn.Create( 423 path.Join(basePath)+SLASH, 424 data, 425 zk.FlagEphemeral|zk.FlagSequence, 426 zk.WorldACL(zk.PermAll), 427 ) 428 } 429 430 if err != nil && err != zk.ErrNodeExists { 431 return "", perrors.WithStack(err) 432 } 433 return tmpPath, nil 434 } 435 436 // GetChildrenW gets children watch by @path 437 func (z *ZookeeperClient) GetChildrenW(path string) ([]string, <-chan zk.Event, error) { 438 conn := z.getConn() 439 if conn == nil { 440 return nil, nil, ErrNilZkClientConn 441 } 442 children, stat, watcher, err := conn.ChildrenW(path) 443 444 if err != nil { 445 return nil, nil, perrors.WithMessagef(err, "Error while invoking zk.ChildrenW(path:%s), the reason maybe is: ", path) 446 } 447 if stat == nil { 448 return nil, nil, perrors.WithMessagef(ErrStatIsNil, "Error while invokeing zk.ChildrenW(path:%s), the reason is: ", path) 449 } 450 451 return children, watcher.EvtCh, nil 452 } 453 454 // GetChildren gets children by @path 455 func (z *ZookeeperClient) GetChildren(path string) ([]string, error) { 456 conn := z.getConn() 457 if conn == nil { 458 return nil, ErrNilZkClientConn 459 } 460 children, stat, err := conn.Children(path) 461 462 if err != nil { 463 return nil, perrors.WithMessagef(err, "Error while invoking zk.Children(path:%s), the reason maybe is: ", path) 464 } 465 if stat == nil { 466 return nil, perrors.Errorf("Error while invokeing zk.Children(path:%s), the reason is that the stat is nil", path) 467 } 468 469 return children, nil 470 } 471 472 // ExistW to judge watch whether it exists or not by @zkPath 473 func (z *ZookeeperClient) ExistW(zkPath string) (<-chan zk.Event, error) { 474 conn := z.getConn() 475 if conn == nil { 476 return nil, ErrNilZkClientConn 477 } 478 _, _, watcher, err := conn.ExistsW(zkPath) 479 480 if err != nil { 481 return nil, perrors.WithMessagef(err, "zk.ExistsW(path:%s)", zkPath) 482 } 483 484 return watcher.EvtCh, nil 485 } 486 487 // GetContent gets content by @zkPath 488 func (z *ZookeeperClient) GetContent(zkPath string) ([]byte, *zk.Stat, error) { 489 return z.Conn.Get(zkPath) 490 } 491 492 // SetContent set content of zkPath 493 func (z *ZookeeperClient) SetContent(zkPath string, content []byte, version int32) (*zk.Stat, error) { 494 return z.Conn.Set(zkPath, content, version) 495 } 496 497 // getConn gets zookeeper connection safely 498 func (z *ZookeeperClient) getConn() *zk.Conn { 499 if z == nil { 500 return nil 501 } 502 z.RLock() 503 defer z.RUnlock() 504 return z.Conn 505 } 506 507 // Reconnect gets zookeeper reconnect event 508 func (z *ZookeeperClient) Reconnect() <-chan struct{} { 509 return z.reconnectCh 510 } 511 512 // GetEventHandler gets zookeeper event handler 513 func (z *ZookeeperClient) GetEventHandler() ZkEventHandler { 514 return z.zkEventHandler 515 } 516 517 func (z *ZookeeperClient) Close() { 518 if z.share { 519 zkClientPool.Lock() 520 defer zkClientPool.Unlock() 521 z.activeNumber-- 522 if z.activeNumber == 0 { 523 z.Conn.Close() 524 delete(zkClientPool.zkClient, z.name) 525 } 526 } else { 527 z.Lock() 528 conn := z.Conn 529 z.activeNumber-- 530 z.Conn = nil 531 z.Unlock() 532 if conn != nil { 533 conn.Close() 534 } 535 } 536 }