Zookeeper API封装

在使用Zookeeper API开发业务时,可以根据具体的业务需求和场景,对API进行封装,以满足高性能、灵活配置和灵活使用的要求。

封装策略:

1.封装连接池

Zookeeper连接的创建和关闭是一项比较耗时的操作,因此可以封装一个连接池,用于管理Zookeeper连接。连接池可以在应用程序启动时初始化,随后在业务逻辑中共享使用,可以提高应用程序的性能和稳定性。在连接池中可以设置连接的最大数量、最大空闲时间等参数,以保证连接的有效性和高效性。

2.封装节点操作API

封装节点操作API可以方便业务开发人员进行节点的增删改查等操作。可以将节点操作封装为函数或方法,并提供相应的参数和返回值。例如,可以封装一个函数,用于创建一个临时顺序节点,并在节点创建成功后返回节点路径。这样可以简化业务逻辑的开发,并提高代码的可读性和可维护性。

3.封装Watcher

Zookeeper提供了Watcher机制,可以在节点状态发生变化时通知客户端。可以将Watcher封装为函数或方法,并提供相应的参数和返回值。例如,可以封装一个函数,用于监视一个节点的状态变化,并在节点状态发生变化时触发相应的事件。这样可以简化业务逻辑的开发,并提高代码的可读性和可维护性。

4.封装ACL操作API

Zookeeper提供了ACL机制,可以对节点进行权限控制。可以将ACL操作封装为函数或方法,并提供相应的参数和返回值。例如,可以封装一个函数,用于设置一个节点的ACL,并在操作成功后返回true。这样可以简化业务逻辑的开发,并提高代码的可读性和可维护性。

总之,通过对Zookeeper API进行封装,可以使业务开发人员更加专注于业务逻辑的开发,提高代码的可读性和可维护性,并且能够灵活配置和使用,以满足业务的高性能和灵活性要求。

实现代码

package zkcli

import (
    "errors"
    "fmt"
    "strings"
    "sync"
    "time"

    "github.com/go-zookeeper/zk"
)

//可配置的,有一个配置文件,viper yaml xml、json
//连接池
//封装接口:API,Watcher、ACL权限相关的

// 连接池
//1.放链接,slice, mutex
//2.最大连接数,maxconn
//3.我们要动态的对连接池进行扩容和缩容
//4.监控:监控到当前连接池的maxconn, 空闲的链接

//客户端
//1. conn,API

type ZKClient struct {
    conn *zk.Conn
}

type ZKClientPool struct {
    clients  []*ZKClient
    mutex    sync.Mutex
    maxConns int
    servers  []string
    timeout  time.Duration
}

//连接池
func NewZkClientPool(servers []string, maxConn int, timeout time.Duration) *ZKClientPool {
    return &ZKClientPool{
        clients:  []*ZKClient{},
        maxConns: maxConn,
        servers:  servers,
        timeout:  timeout,
    }
}

func (pool *ZKClientPool) GetClient() (*ZKClient, error) {
    pool.mutex.Lock()
    defer pool.mutex.Unlock()

    if len(pool.clients) > 0 {
        client := pool.clients[0]
        pool.clients = pool.clients[1:]
        return client, nil
    }

    conn, _, err := zk.Connect(pool.servers, pool.timeout)
    if err != nil {
        return nil, err
    }

    client := &ZKClient{
        conn: conn,
    }

    pool.clients = append(pool.clients, client)

    return client, nil
}

//回收conn
func (pool *ZKClientPool) ReleaseClient(client *ZKClient) {
    pool.mutex.Lock()
    defer pool.mutex.Unlock()

    if len(pool.clients) >= pool.maxConns {
        client.conn.Close()
        return
    }
    pool.clients = append(pool.clients, client)
}

//创建持久化节点
func (client *ZKClient) CreatePersistentNode(path string, data string) ([]byte, error) {
    //需要考虑没有子结点的情况,
    res, err := client.conn.Create(path, []byte(data), 0, zk.WorldACL(zk.PermAll))
    if err != nil {
        return nil, err
    }
    return []byte(res), nil
}

//创建持久化顺序节点
func (client *ZKClient) CreatePersistenceSequenceNode(path string, data string) ([]byte, error) {
    //需要考虑没有子结点的情况,
    res, err := client.conn.Create(path, []byte(data), 2, zk.WorldACL(zk.PermAll))
    if err != nil {
        return nil, err
    }
    return []byte(res), nil
}

//创建临时结点
func (client *ZKClient) CreateEphemeralNode(path string, data string) ([]byte, error) {
    res, err := client.conn.Create(path, []byte(data), 1, zk.WorldACL(zk.PermAll))
    if err != nil {
        return nil, err
    }
    return []byte(res), nil
}

//创建一个有序的临时结点
func (client *ZKClient) CreateEphemeralSequentialNode(path string, data []byte, acl []zk.ACL) (string, error) {

    if path == "" {
        return "", errors.New("Path is empty")
    } else if path == "/" {
        return "", errors.New("Invalid node path")
    }

    // ensure the parent path exists
    // parentPath := strings.TrimSuffix(path, fmt.Sprintf("/%s", zk.DefaultEphemeralPrefix))
    // err := client.createParentPath(parentPath)
    // if err != nil {
    //     return "", err
    // }

    nodePath, err := client.conn.CreateProtectedEphemeralSequential(path, data, acl)
    if err != nil {
        return "", err
    }
    return nodePath, nil
}

func (client *ZKClient) createParentPath(path string) error {
    if path == "" {
        return nil
    }

    exists, _, err := client.conn.Exists(path)
    if err != nil {
        return err
    }
    if exists {
        return nil
    }
    err = client.createParentPath(strings.TrimSuffix(path, "/"+strings.TrimPrefix(path, "/")))
    if err != nil {
        return err
    }

    _, err = client.conn.Create(path, []byte{}, 0, zk.WorldACL(zk.PermAll))
    if err != nil && err != zk.ErrNodeExists {
        return err
    }
    return nil
}

//获取当前结点下的子节点
func (client *ZKClient) GetChildren(path string) ([]string, error) {
    children, _, err := client.conn.Children(path)
    if err != nil {
        return nil, err
    }
    return children, nil
}

//监听一个节点下子结点的变化
func (client *ZKClient) ChildrenWatch(path string, f func()) (func(), error) {
    _, _, events, err := client.conn.ChildrenW(path)
    if err != nil {
        return nil, err
    }
    var stopCh chan struct{}
    //监听孩子改变
    go func() {
        for {
            select {
            case event := <-events:
                if event.Err != nil {
                    fmt.Printf("ChildrenWatch error: %v\n", event.Err)
                    return
                }
                //子几点变化了,则执行回调方法
                if event.Type == zk.EventNodeChildrenChanged {
                    f()
                }

                //然后再次创建监听
                _, _, events, err = client.conn.ChildrenW(path)
                if err != nil {
                    fmt.Printf("ChildrenWatch error: %v\n", err)
                    return
                }
            case <-stopCh:
                goto STOP
            }
        }
    STOP:
        return
    }()

    //返回方法,关闭监听
    return func() {
        stopCh <- struct{}{}

    }, nil
}

//获取一个节点的值
func (client *ZKClient) GetNodeData(path string) ([]byte, error) {
    data, _, err := client.conn.Get(path)
    if err != nil {
        return nil, err
    }
    return data, nil
}

func (client *ZKClient) NodeDataWatch(path string, f func()) (func(), error) {
    _, _, events, err := client.conn.GetW(path)
    if err != nil {
        return nil, err
    }
    //监听孩子结点改变
    var stopCh chan struct{}
    go func() {
        for {
            select {
            case event := <-events:
                if event.Err != nil {
                    fmt.Printf("NodeDataWatch error: %v\n", event.Err)
                    return
                }

                if event.Type == zk.EventNodeDataChanged {
                    f()
                }

                // recreate watch
                _, _, events, err = client.conn.GetW(path)
                if err != nil {
                    fmt.Printf("NodeDataWatch error: %v\n", err)
                    return
                }
            case <-stopCh:
                goto STOP
            }
        }
    STOP:
        return
    }()

    return func() {
        stopCh <- struct{}{}
    }, nil
}

func (client *ZKClient) Close() {
    client.conn.Close()
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值