ZooKeeper是分布式协调服务,用处非常广,例如消息系统kafka、大数据hadoop、分布式数据库hbase、搜索引擎solr、分布式资源管理框架mesos等。同类产品有etcd、consul、eureka,大体功能是服务注册和发现,比如用在微服务框架中。ZooKeeper源码是用java来实现的,第三方比较好用的客户端也是java实现的,比如curator和zkclient。
在编写go示例代码前,先安装zookeeper包:
# go get github.com/samuel/go-zookeeper/zk
请看示例代码:
package main
import (
"fmt"
"github.com/samuel/go-zookeeper/zk"
"log"
"os"
"time"
)
// 定义常量
const (
AUTH_USER = "fullstackcareer"
AUTH_PWD = "FullStackCareer"
PATH = "/fullstackcareer"
DATA1 = "data1"
DATA2 = "data2"
)
// 定义客户端结构体
type ZkClient struct {
ConnectString []string
ZkConn *zk.Conn
zkAcl []zk.ACL
}
// 新建客户端
func NewZkClient(connectString []string) *ZkClient {
client :=ZkClient{
ConnectString: connectString[:],
ZkConn: nil,
zkAcl: zk.DigestACL(zk.PermAll, AUTH_USER, AUTH_PWD),
}
return &client
}
// 关闭客户端
func (client *ZkClient) Close() error {
if client.ZkConn != nil {
client.ZkConn.Close()
}
return nil
}
// 创建连接
func(client *ZkClient) Connect(sessionTimeout time.Duration) error {
// 判断连接是否存在
if client.ZkConn != nil {
client.Close()
}
// 创建连接
c, _, err :=zk.Connect(client.ConnectString, sessionTimeout)
if err != nil {
return err
}
// ACL 认证
auth :=AUTH_USER + ":" + AUTH_PWD
if err := c.AddAuth("digest", []byte(auth)); err != nil {
client.Close()
return err
}
// 赋值到结构体变量,复用该连接
client.ZkConn = c
return nil
}
// 创建 znode
func (client *ZkClient) Create(path string, data []byte) error {
_, err := client.ZkConn.Create(path, data, 0, client.zkAcl)
return err
}
// 判断 znode 是否存在
func (client *ZkClient) Exist(path string) (bool, error) {
isExist, _, err := client.ZkConn.Exists(path)
return isExist, err
}
// 设置 znode 值
func (client *ZkClient) Set(path string, data []byte, version int32) error {
_, err :=client.ZkConn.Set(path, data, version)
return err
}
// 获取 znode 的值
func (client *ZkClient) Get(path string) (string, error) {
data, _, err := client.ZkConn.Get(path)
return string(data), err
}
// 删除 znode
func (client *ZkClient) delete(path string) error {
err := client.ZkConn.Delete(path, -1)
return err
}
func main() {
// 传参合法性判断
if len(os.Args) != 2 {
fmt.Println("usage: ", os.Args[0], "connectString")
os.Exit(-1)
}
connectString := []string{os.Args[1]}
// 创建客户端
zkClient := NewZkClient(connectString)
if zkClient != nil {
log.Println("创建zookeeper客户端成功")
} else {
log.Fatal("创建zookeeper客户端失败")
}
// 创建连接
err := zkClient.Connect(60 * time.Second)
if err != nil {
log.Fatal("创建zookeeper连接失败")
} else {
log.Println("创建zookeeper连接成功")
}
// 创建 znode
err = zkClient.Create(PATH, []byte(DATA1))
if err != nil {
log.Fatal("创建 " + PATH + " 失败")
} else {
log.Println("创建 " + PATH + " 成功")
}
// 判断 znode 是否存在
isExist, err := zkClient.Exist(PATH)
if err == nil {
if isExist == true {
log.Println("znode " + PATH + " 存在")
} else {
log.Fatal("znode " + PATH + " 不存在")
}
}
// 设置 znode 的值
err = zkClient.Set(PATH, []byte(DATA2), -1)
if err != nil {
log.Fatal("设置 znode " + PATH + " 值 " + DATA2 + " 失败")
} else {
log.Println("设置 znode " + PATH + " 值 " + DATA2 + " 成功")
}
// 获取 znode 的值
data, err := zkClient.Get(PATH)
if err != nil {
log.Fatal("获取 znode " + PATH + "值失败")
} else {
log.Print("获取 znode " + PATH + "值为 " + data)
}
// 删除 znode
err = zkClient.delete(PATH)
if err != nil {
log.Fatal("删除 znode " + PATH + " 失败")
} else {
log.Println("删除 znode " + PATH + " 成功")
}
// 关闭客户端
err = zkClient.Close()
if err == nil {
log.Println("关闭客户端成功")
}
}
编译代码,执行:
# go build zookeeper.go
# ./zookeeper localhost:2181
2020/02/16 17:44:13 创建zookeeper客户端成功
2020/02/16 17:44:13 Connected to [::1]:2181
2020/02/16 17:44:13 authenticated: id=72057621439774727, timeout=40000
2020/02/16 17:44:13 re-submitting `0` credentials after reconnect
2020/02/16 17:44:13 创建zookeeper连接成功
2020/02/16 17:44:13 创建 /fullstackcareer 成功
2020/02/16 17:44:13 znode /fullstackcareer 存在
2020/02/16 17:44:13 设置 znode /fullstackcareer 值 data2 成功
2020/02/16 17:44:13 获取 znode /fullstackcareer值为 data2
2020/02/16 17:44:13 删除 znode /fullstackcareer 成功
2020/02/16 17:44:13 recv loop terminated: err=EOF
2020/02/16 17:44:13 send loop terminated: err=<nil>
2020/02/16 17:44:13 关闭客户端成功
总结
本文的示例代码实现了对ZooKeeper常见的操作,包括创建客户端、创建连接、创建znode、判断znode是否存在、更新znode值、获取znode值、关闭客户端。需要注意的是,文中的代码是利用面向对象的思维设计的,在实际工作中也有参考意义。