github.heygears.com/openimsdk/tools@v0.0.49/discovery/zookeeper/zk.go (about)

     1  // Copyright © 2023 OpenIM. All rights reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package zookeeper
    16  
    17  import (
    18  	"context"
    19  	"net"
    20  	"strconv"
    21  	"sync"
    22  	"time"
    23  
    24  	"github.com/go-zookeeper/zk"
    25  	"github.com/openimsdk/tools/errs"
    26  	"github.com/openimsdk/tools/log"
    27  	"google.golang.org/grpc"
    28  	"google.golang.org/grpc/resolver"
    29  )
    30  
    31  const (
    32  	defaultFreq = time.Minute * 30
    33  	timeout     = 5
    34  )
    35  
    36  type ZkClient struct {
    37  	ZkServers []string
    38  	zkRoot    string
    39  	username  string
    40  	password  string
    41  
    42  	rpcRegisterName string
    43  	rpcRegisterAddr string
    44  	isRegistered    bool
    45  	scheme          string
    46  
    47  	timeout   int
    48  	conn      *zk.Conn
    49  	eventChan <-chan zk.Event
    50  	node      string
    51  	ticker    *time.Ticker
    52  
    53  	lock    sync.Locker
    54  	options []grpc.DialOption
    55  
    56  	resolvers           map[string]*Resolver
    57  	localConns          map[string][]*grpc.ClientConn
    58  	cancel              context.CancelFunc
    59  	isStateDisconnected bool
    60  	balancerName        string
    61  
    62  	logger log.Logger
    63  }
    64  
    65  // NewZkClient initializes a new ZkClient with provided options and establishes a Zookeeper connection.
    66  func NewZkClient(ZkServers []string, scheme string, options ...ZkOption) (*ZkClient, error) {
    67  	client := &ZkClient{
    68  		ZkServers:  ZkServers,
    69  		zkRoot:     "/",
    70  		scheme:     scheme,
    71  		timeout:    timeout,
    72  		localConns: make(map[string][]*grpc.ClientConn),
    73  		resolvers:  make(map[string]*Resolver),
    74  		lock:       &sync.Mutex{},
    75  		logger:     nilLog{},
    76  	}
    77  	for _, option := range options {
    78  		option(client)
    79  	}
    80  
    81  	// Establish a Zookeeper connection with a specified timeout and handle authentication.
    82  	conn, eventChan, err := zk.Connect(ZkServers, time.Duration(client.timeout)*time.Second, zk.WithLogger(nilLog{}))
    83  	if err != nil {
    84  		return nil, errs.WrapMsg(err, "connect failed", "ZkServers", ZkServers)
    85  	}
    86  
    87  	ctx, cancel := context.WithCancel(context.Background())
    88  	client.cancel = cancel
    89  	client.ticker = time.NewTicker(defaultFreq)
    90  
    91  	// Ensure authentication is set if credentials are provided.
    92  	if client.username != "" && client.password != "" {
    93  		auth := []byte(client.username + ":" + client.password)
    94  		if err := conn.AddAuth("digest", auth); err != nil {
    95  			conn.Close()
    96  			return nil, errs.WrapMsg(err, "AddAuth failed", "username", client.username, "password", client.password)
    97  		}
    98  	}
    99  
   100  	client.zkRoot += scheme
   101  	client.eventChan = eventChan
   102  	client.conn = conn
   103  
   104  	// Verify root node existence and create if missing.
   105  	if err := client.ensureRoot(); err != nil {
   106  		conn.Close()
   107  		return nil, err
   108  	}
   109  
   110  	resolver.Register(client)
   111  	go client.refresh(ctx)
   112  	go client.watch(ctx)
   113  
   114  	return client, nil
   115  }
   116  
   117  func (s *ZkClient) Close() {
   118  	s.logger.Info(context.Background(), "close zk called")
   119  	s.cancel()
   120  	s.ticker.Stop()
   121  	s.conn.Close()
   122  }
   123  
   124  func (s *ZkClient) ensureAndCreate(node string) error {
   125  	exists, _, err := s.conn.Exists(node)
   126  	if err != nil {
   127  		return errs.WrapMsg(err, "Exists failed", "node", node)
   128  	}
   129  	if !exists {
   130  		_, err = s.conn.Create(node, []byte(""), 0, zk.WorldACL(zk.PermAll))
   131  		if err != nil && err != zk.ErrNodeExists {
   132  			return errs.WrapMsg(err, "Create failed", "node", node)
   133  		}
   134  	}
   135  	return nil
   136  }
   137  
   138  func (s *ZkClient) refresh(ctx context.Context) {
   139  	for range s.ticker.C {
   140  		s.logger.Debug(ctx, "zk refresh local conns")
   141  		s.lock.Lock()
   142  		for rpcName := range s.resolvers {
   143  			s.flushResolver(rpcName)
   144  		}
   145  		for rpcName := range s.localConns {
   146  			delete(s.localConns, rpcName)
   147  		}
   148  		s.lock.Unlock()
   149  		s.logger.Debug(ctx, "zk refresh local conns success")
   150  	}
   151  }
   152  
   153  func (s *ZkClient) flushResolverAndDeleteLocal(serviceName string) {
   154  	s.logger.Debug(context.Background(), "zk start flush", "serviceName", serviceName)
   155  	s.flushResolver(serviceName)
   156  	delete(s.localConns, serviceName)
   157  }
   158  
   159  func (s *ZkClient) flushResolver(serviceName string) {
   160  	r, ok := s.resolvers[serviceName]
   161  	if ok {
   162  		r.ResolveNowZK(resolver.ResolveNowOptions{})
   163  	}
   164  }
   165  
   166  func (s *ZkClient) GetZkConn() *zk.Conn {
   167  	return s.conn
   168  }
   169  
   170  func (s *ZkClient) GetRootPath() string {
   171  	return s.zkRoot
   172  }
   173  
   174  func (s *ZkClient) GetNode() string {
   175  	return s.node
   176  }
   177  
   178  func (s *ZkClient) ensureRoot() error {
   179  	return s.ensureAndCreate(s.zkRoot)
   180  }
   181  
   182  func (s *ZkClient) ensureName(rpcRegisterName string) error {
   183  	return s.ensureAndCreate(s.getPath(rpcRegisterName))
   184  }
   185  
   186  func (s *ZkClient) getPath(rpcRegisterName string) string {
   187  	return s.zkRoot + "/" + rpcRegisterName
   188  }
   189  
   190  func (s *ZkClient) getAddr(host string, port int) string {
   191  	return net.JoinHostPort(host, strconv.Itoa(port))
   192  }
   193  
   194  func (s *ZkClient) AddOption(opts ...grpc.DialOption) {
   195  	s.options = append(s.options, opts...)
   196  }
   197  
   198  func (s *ZkClient) GetClientLocalConns() map[string][]*grpc.ClientConn {
   199  	return s.localConns
   200  }