在使用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()
}