go操作zookeeper

基本使用

连接至zookeeper

 /* 获取一个zk连接
 * @return {[type]}
 */
func getConnect(zkList []string) (conn *zk.Conn) {
    conn, _, err := zk.Connect(zkList, 10*time.Second)
    if err != nil {
        fmt.Println(err)
    }
    return
}

新增

/* 测试连接
 * @return
 */
func test1() {
    zkList := []string{"localhost:2181"}
    conn := getConnect(zkList)

    defer conn.Close()
    var flags int32 = 0
    //flags有4种取值:
    //0:永久,除非手动删除
    //zk.FlagEphemeral = 1:短暂,session断开则改节点也被删除
    //zk.FlagSequence  = 2:会自动在节点后面添加序号
    //3:Ephemeral和Sequence,即,短暂且自动添加序号
    conn.Create("/go_servers", nil, flags, zk.WorldACL(zk.PermAll)) // zk.WorldACL(zk.PermAll)控制访问权限模式

    time.Sleep(20 * time.Second)
}

注册方式

var flags int32 = 0
flags有4种取值:

  • 0:永久,除非手动删除
  • zk.FlagEphemeral = 1:短暂,session断开则改节点也被删除
  • zk.FlagSequence = 2:会自动在节点后面添加序号
  • 3:Ephemeral和Sequence,即,短暂且自动添加序号

​ conn.Create(“/go_servers”, nil, flags, zk.WorldACL(zk.PermAll)) // zk.WorldACL(zk.PermAll)控制访问权限模式

更改和删除

//删改与增不同在于其函数中的version参数,其中version是用于 CAS支持
func (c *Conn) Set(path string, data []byte, version int32) (*Stat, error)
func (c *Conn) Delete(path string, version int32) error

demo:
if err = conn.Delete(migrateLockPath, -1); err != nil {
    log.Error("conn.Delete(\"%s\") error(%v)", migrateLockPath, err)
}
func main() {
    // 连接到 ZooKeeper
    servers := []string{"127.0.0.1:2181"} // 这里需要替换为你的 ZooKeeper 实例的地址
    conn, _, err := zk.Connect(servers, time.Second) // 连接超时时间
    if err != nil {
        log.Fatalf("Failed to connect to ZooKeeper: %v", err)
    }
    defer conn.Close()

    // ZooKeeper 操作
    path := "/example/message" // 在 ZooKeeper 中的路径
    data := []byte("Hello ZooKeeper!") // 要保存的消息内容
    
    // 创建节点并保存消息
    flags := int32(0) // 标记:0 表示持久节点,zk.FlagEphemeral 表示临时节点
    acl := zk.WorldACL(zk.PermAll) // 权限:全世界可读写
    createdPath, err := conn.Create(path, data, flags, acl)
    if err != nil {
        log.Fatalf("Failed to create node: %v", err)
    }
    fmt.Printf("Node created: %s\n", createdPath)
    
    // 读取节点数据
    storedData, _, err := conn.Get(path)
    if err != nil {
        log.Fatalf("Failed to get node data: %v", err)
    }
    fmt.Printf("Stored message: %s\n", string(storedData))

}

监听key的变化

可以通过设置“监听器”(watcher)来监听某个节点(key)的变化。当节点的数据发生变化、节点被删除或子节点发生变化时,监听器会被触发

package main

import (
    "github.com/samuel/go-zookeeper/zk"

)

func main() {
    // 连接到 ZooKeeper
    servers := []string{"127.0.0.1:2181"} // 替换为你的 ZooKeeper 实例的地址
    conn, _, err := zk.Connect(servers, time.Second)
    if err != nil {
        log.Fatalf("Failed to connect to ZooKeeper: %v", err)
    }
    defer conn.Close()

    // 要监听的节点路径
    path := "/example/message"
    
    // 获取节点数据并设置监听器
    data, _, ch, err := conn.GetW(path)
    if err != nil {
        log.Fatalf("Failed to get data for node: %v", err)
    }
    fmt.Printf("Current data: %s\n", string(data))
    
    // 等待事件发生
    for {
        select {
        case event := <-ch:
            if event.Type == zk.EventNodeDataChanged {
                fmt.Println("Node data changed, fetching new data...")
    
                // 获取新数据
                newData, _, newCh, err := conn.GetW(path)
                if err != nil {
                    log.Fatalf("Failed to get updated data for node: %v", err)
                }
                fmt.Printf("Updated data: %s\n", string(newData))
    
                // 更新监听通道
                ch = newCh
            } else if event.Type == zk.EventNodeDeleted {
                fmt.Println("Node deleted")
                return
            }
        }
    }

}

获取所有节点

func test3() {
    zkList := []string{"localhost:2181"}
    conn := getConnect(zkList)

    defer conn.Close()

    children, _, err := conn.Children("/go_servers")
    if err != nil {
        fmt.Println(err)
    }
    fmt.Printf("%v \n", children)
}

实现分布式锁node

以下是一个简单的示例,展示如何使用 ZooKeeper 实现分布式锁:

go复制代码package main

import (
    "fmt"
    "log"
    "time"

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

type DistributedLock struct {
    conn      *zk.Conn
    lockPath  string
    lockNode  string
    ephemeral bool
}

// 创建一个新的分布式锁
func NewDistributedLock(conn *zk.Conn, lockPath string) *DistributedLock {
    return &DistributedLock{
        conn:     conn,
        lockPath: lockPath,
    }
}

// 获取锁
func (dl *DistributedLock) Lock() error {
    // 创建持久顺序节点
    nodePath := fmt.Sprintf("%s/lock_", dl.lockPath)
    node, err := dl.conn.CreateProtectedEphemeralSequential(nodePath, []byte{}, zk.WorldACL(zk.PermAll))
    if err != nil {
        return fmt.Errorf("failed to create lock node: %v", err)
    }
    dl.lockNode = node

    // 获取当前锁节点下的所有子节点
    children, _, err := dl.conn.Children(dl.lockPath)
    if err != nil {
        return fmt.Errorf("failed to get children: %v", err)
    }

    // 获取序号最小的节点
    minNode := node
    for _, child := range children {
        if child < minNode {
            minNode = child
        }
    }

    // 如果当前节点是序号最小的节点,获取锁
    if node == minNode {
        fmt.Println("Lock acquired!")
        return nil
    }

    // 如果不是最小节点,等待前一个节点的删除
    prevNode := ""
    for _, child := range children {
        if child < node && (prevNode == "" || child > prevNode) {
            prevNode = child
        }
    }

    // 监听前一个节点的删除事件
    prevNodePath := fmt.Sprintf("%s/%s", dl.lockPath, prevNode)
    _, _, ch, err := dl.conn.ExistsW(prevNodePath)
    if err != nil {
        return fmt.Errorf("failed to set watch on previous node: %v", err)
    }

    // 等待前一个节点被删除
    event := <-ch
    if event.Type == zk.EventNodeDeleted {
        fmt.Println("Lock acquired!")
        return nil
    }

    return fmt.Errorf("failed to acquire lock")
}

// 释放锁
func (dl *DistributedLock) Unlock() error {
    if dl.lockNode == "" {
        return fmt.Errorf("lock not acquired")
    }

    err := dl.conn.Delete(dl.lockNode, -1)
    if err != nil {
        return fmt.Errorf("failed to release lock: %v", err)
    }

    fmt.Println("Lock released!")
    return nil
}

func main() {
    servers := []string{"127.0.0.1:2181"} // 替换为你的 ZooKeeper 实例的地址
    conn, _, err := zk.Connect(servers, time.Second)
    if err != nil {
        log.Fatalf("Failed to connect to ZooKeeper: %v", err)
    }
    defer conn.Close()

    lockPath := "/distributed_lock"
    lock := NewDistributedLock(conn, lockPath)

    err = lock.Lock()
    if err != nil {
        log.Fatalf("Failed to acquire lock: %v", err)
    }

    // 在这里执行需要同步的操作
    time.Sleep(2 * time.Second)

    err = lock.Unlock()
    if err != nil {
        log.Fatalf("Failed to release lock: %v", err)
    }
}

服务注册与发现

目前分布式系统已经很流行了,一些开源框架也被广泛应用,如dubbo、Motan等。对于一个分布式服务,最基本的一项功能就是服务的注册和发现,而利用zk的EPHEMERAL节点则可以很方便的实现该功能。EPHEMERAL节点正如其名,是临时性的,其生命周期是和客户端会话绑定的,当会话连接断开时,节点也会被删除。下边我们就来实现一个简单的分布式server:

server:

服务启动时,创建zk连接,并在go_servers节点下创建一个新节点,节点名为"ip:port",完成服务注册 服务结束时,由于连接断开,创建的节点会被删除,这样client就不会连到该节点

server.go

package main

import (
    "github.com/samuel/go-zookeeper/zk"
)

func main() {
    go starServer("127.0.0.1:8897")
    go starServer("127.0.0.1:8898")
    go starServer("127.0.0.1:8899")

    a := make(chan bool, 1)
    <-a
}

func checkError(err error) {
    if err != nil {
        fmt.Println(err)
    }
}

func starServer(port string) {
    tcpAddr, err := net.ResolveTCPAddr("tcp4", port)
    fmt.Println(tcpAddr)
    checkError(err)

    listener, err := net.ListenTCP("tcp", tcpAddr)
    checkError(err)

    //注册zk节点q
    // 链接zk
    conn, err := GetConnect()
    if err != nil {
        fmt.Printf(" connect zk error: %s ", err)
    }
    defer conn.Close()
    // zk节点注册
    err = RegistServer(conn, port)
    if err != nil {
        fmt.Printf(" regist node error: %s ", err)
    }

    for {
        conn, err := listener.Accept()
        if err != nil {
            fmt.Fprintf(os.Stderr, "Error: %s", err)
            continue
        }
        go handleCient(conn, port)
    }

    fmt.Println("aaaaaa")
}

func handleCient(conn net.Conn, port string) {
    defer conn.Close()

    daytime := time.Now().String()
    conn.Write([]byte(port + ": " + daytime))
}
func GetConnect() (conn *zk.Conn, err error) {
    zkList := []string{"localhost:2181"}
    conn, _, err = zk.Connect(zkList, 10*time.Second)
    if err != nil {
        fmt.Println(err)
    }
    return
}

func RegistServer(conn *zk.Conn, host string) (err error) {
    _, err = conn.Create("/go_servers/"+host, nil, zk.FlagEphemeral, zk.WorldACL(zk.PermAll))
    return
}

func GetServerList(conn *zk.Conn) (list []string, err error) {
    list, _, err = conn.Children("/go_servers")
    return
}

client:

先从zk获取go_servers节点下所有子节点,这样就拿到了所有注册的server 从server列表中选中一个节点(这里只是随机选取,实际服务一般会提供多种策略),创建连接进行通信 这里为了演示,我们每次client连接server,获取server发送的时间后就断开。主要代码如下:

client.go

package main

import (
    "github.com/samuel/go-zookeeper/zk"
)

func checkError(err error) {
    if err != nil {
        fmt.Println(err)
    }
}
func main() {
    for i := 0; i < 100; i++ {
        startClient()

        time.Sleep(1 * time.Second)
    }
}

func startClient() {
    // service := "127.0.0.1:8899"
    //获取地址
    serverHost, err := getServerHost()
    if err != nil {
        fmt.Printf("get server host fail: %s \n", err)
        return
    }

    fmt.Println("connect host: " + serverHost)
    tcpAddr, err := net.ResolveTCPAddr("tcp4", serverHost)
    checkError(err)
    conn, err := net.DialTCP("tcp", nil, tcpAddr)
    checkError(err)
    defer conn.Close()

    _, err = conn.Write([]byte("timestamp"))
    checkError(err)

    result, err := ioutil.ReadAll(conn)
    checkError(err)
    fmt.Println(string(result))

    return
}

func getServerHost() (host string, err error) {
    conn, err := GetConnect()
    if err != nil {
        fmt.Printf(" connect zk error: %s \n ", err)
        return
    }
    defer conn.Close()
    serverList, err := GetServerList(conn)
    if err != nil {
        fmt.Printf(" get server list error: %s \n", err)
        return
    }

    count := len(serverList)
    if count == 0 {
        err = errors.New("server list is empty \n")
        return
    }

    //随机选中一个返回
    r := rand.New(rand.NewSource(time.Now().UnixNano()))
    host = serverList[r.Intn(3)]
    return
}
func GetConnect() (conn *zk.Conn, err error) {
    zkList := []string{"localhost:2181"}
    conn, _, err = zk.Connect(zkList, 10*time.Second)
    if err != nil {
        fmt.Println(err)
    }
    return
}
func GetServerList(conn *zk.Conn) (list []string, err error) {
    list, _, err = conn.Children("/go_servers")
    return
}

参考
https://topgoer.com/%E6%95%B0%E6%8D%AE%E5%BA%93%E6%93%8D%E4%BD%9C/zookeeper/

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值