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  }