You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Open-IM-Server/pkg/discoveryregistry/zookeeper/zk.go

192 lines
3.9 KiB

package zookeeper
import (
"context"
"net"
"strconv"
"sync"
"time"
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/log"
"github.com/go-zookeeper/zk"
"google.golang.org/grpc"
"google.golang.org/grpc/resolver"
)
const (
defaultFreq = time.Minute * 30
timeout = 5
)
type ZkClient struct {
zkServers []string
zkRoot string
userName string
password string
scheme string
timeout int
conn *zk.Conn
eventChan <-chan zk.Event
node string
ticker *time.Ticker
lock sync.RWMutex
options []grpc.DialOption
resolvers map[string]*Resolver
localConns map[string][]resolver.Address
balancerName string
RoundRobin
}
type ZkOption func(*ZkClient)
func WithRoundRobin() ZkOption {
return func(client *ZkClient) {
client.balancerName = "round_robin"
}
}
func WithUserNameAndPassword(userName, password string) ZkOption {
return func(client *ZkClient) {
client.userName = userName
client.password = password
}
}
func WithOptions(opts ...grpc.DialOption) ZkOption {
return func(client *ZkClient) {
client.options = opts
}
}
func WithFreq(freq time.Duration) ZkOption {
return func(client *ZkClient) {
client.ticker = time.NewTicker(freq)
}
}
func WithTimeout(timeout int) ZkOption {
return func(client *ZkClient) {
client.timeout = timeout
}
}
func NewClient(zkServers []string, zkRoot string, options ...ZkOption) (*ZkClient, error) {
client := &ZkClient{
zkServers: zkServers,
zkRoot: "/",
scheme: zkRoot,
timeout: timeout,
localConns: make(map[string][]resolver.Address),
resolvers: make(map[string]*Resolver),
}
client.ticker = time.NewTicker(defaultFreq)
for _, option := range options {
option(client)
}
conn, eventChan, err := zk.Connect(zkServers, time.Duration(client.timeout)*time.Second, zk.WithLogInfo(false))
if err != nil {
return nil, err
}
if client.userName != "" && client.password != "" {
if err := conn.AddAuth("digest", []byte(client.userName+":"+client.password)); err != nil {
return nil, err
}
}
client.zkRoot += zkRoot
client.eventChan = eventChan
client.conn = conn
if err := client.ensureRoot(); err != nil {
client.CloseZK()
return nil, err
}
resolver.Register(client)
var wg sync.WaitGroup
wg.Add(2)
go client.refresh(&wg)
go client.watch(&wg)
wg.Wait()
return client, nil
}
func (s *ZkClient) CloseZK() {
s.conn.Close()
}
func (s *ZkClient) ensureAndCreate(node string) error {
exists, _, err := s.conn.Exists(node)
if err != nil {
return err
}
if !exists {
_, err := s.conn.Create(node, []byte(""), 0, zk.WorldACL(zk.PermAll))
if err != nil && err != zk.ErrNodeExists {
return err
}
}
return nil
}
func (s *ZkClient) refresh(wg *sync.WaitGroup) {
wg.Done()
for range s.ticker.C {
log.ZDebug(context.Background(), "refresh local conns")
s.lock.Lock()
for rpcName := range s.resolvers {
s.flushResolver(rpcName)
}
for rpcName := range s.localConns {
delete(s.localConns, rpcName)
}
s.lock.Unlock()
}
}
func (s *ZkClient) flushResolver(serviceName string) {
r, ok := s.resolvers[serviceName]
if ok {
r.ResolveNow(resolver.ResolveNowOptions{})
s.resolvers[serviceName] = r
}
}
func (s *ZkClient) GetZkConn() *zk.Conn {
return s.conn
}
func (s *ZkClient) GetRootPath() string {
return s.zkRoot
}
func (s *ZkClient) GetNode() string {
return s.node
}
func (s *ZkClient) ensureRoot() error {
return s.ensureAndCreate(s.zkRoot)
}
func (s *ZkClient) ensureName(rpcRegisterName string) error {
return s.ensureAndCreate(s.getPath(rpcRegisterName))
}
func (s *ZkClient) getPath(rpcRegisterName string) string {
return s.zkRoot + "/" + rpcRegisterName
}
func (s *ZkClient) getAddr(host string, port int) string {
return net.JoinHostPort(host, strconv.Itoa(port))
}
func (s *ZkClient) AddOption(opts ...grpc.DialOption) {
s.options = append(s.options, opts...)
}
func (s *ZkClient) GetClientLocalConns() map[string][]resolver.Address {
return s.localConns
}