1.启动zookeeper
进入容器内部
2.封装核心方法
package curd
import (
"fmt"
"time"
"github.com/samuel/go-zookeeper/zk"
)
type ZKServer struct {
hosts []string
conn *zk.Conn
pathPrefix string
}
//创建结构体
func NewZKServer(hosts []string) (*ZKServer, error) {
conn, _, err := zk.Connect(hosts, 5*time.Second)
if err != nil {
return nil, err
}
return &ZKServer{hosts: hosts, conn: conn, pathPrefix: "/zk_service_"}, nil
}
// Close 关闭服务
func (z *ZKServer) Close() {
z.conn.Close()
}
// GetPathData 获取配置
func (z *ZKServer) GetDataByPath(path string) ([]byte, *zk.Stat, error) {
return z.conn.Get(path)
}
// UpdateDataByPath 有则更新、无则新建配置
// UpdateDataByPath 有则更新、无则新建节点并配置内容
func (z *ZKServer) UpdateDataByPath(path string, data []byte, version int32) (err error) {
ex, _, _ := z.conn.Exists(path)
if !ex {
z.conn.Create(path, data, 0, zk.WorldACL(zk.PermAll))
return nil
}
// 需要版本才能更新
_, stat, err := z.GetDataByPath(path)
if err != nil {
return
}
_, err = z.conn.Set(path, data, stat.Version)
return
}
func (z *ZKServer) WatchHostsByPath(path string) (chan []string, chan error) {
snapshots := make(chan []string) // 变动后的挂载目标列表
errors := make(chan error) // 变动错误信息
go func() {
for {
snapshot, _, events, err := z.conn.ChildrenW(path)
if err != nil {
errors <- err
}
snapshots <- snapshot
select {
case evt := <-events:
if evt.Err != nil {
errors <- evt.Err
}
if evt.Type == zk.EventNodeCreated {
fmt.Printf("has node[%s] detete\n", evt.Path)
} else if evt.Type == zk.EventNodeDeleted {
fmt.Printf("has new node[%d] create\n", evt.Path)
} else if evt.Type == zk.EventNodeDataChanged {
fmt.Printf("has node[%d] data changed", evt.Path)
}
fmt.Printf("ChildrenW Event Path:%v, Type:%v\n", evt.Path, evt.Type)
}
}
}()
return snapshots, errors
}
func (z *ZKServer) WatchDataByPath(path string) (chan []byte, chan error) {
snapshots := make(chan []byte)
errors := make(chan error)
go func() {
for {
data, _, events, err := z.conn.GetW(path)
if err != nil {
errors <- err
}
snapshots <- data
select {
case evt := <-events:
if evt.Err != nil {
errors <- evt.Err
return
}
fmt.Printf("GetW Event Path:%v, Type:%v\n", evt.Path, evt.Type)
}
}
}()
return snapshots, errors
}
// RegistHostOnPath 将主机挂载在路径上(节点上)
func (z *ZKServer) RegistHostOnPath(path string, host string) (err error) {
// 1. 若路径不存在则新建
ex, _, err := z.conn.Exists(path)
if err != nil {
return
}
if !ex {
_, err = z.conn.Create(path, nil, 0, zk.WorldACL(zk.PermAll))
if err != nil {
return
}
}
// 2. 将主机进行挂载(路径为永久、主机为临时); 临时会检测主机的存活并清理
subNodePath := fmt.Sprintf("%s/%s", path, host)
// 2.1 主机是否已挂载, 没挂则挂
if ex, _ := z.Exists(subNodePath); !ex {
_, err = z.conn.Create(subNodePath, nil, zk.FlagEphemeral, zk.WorldACL(zk.PermAll))
}
return
}
// GetListByPath 获取节点下挂载的列表
func (z *ZKServer) GetListByPath(path string) (list []string, err error) {
list, _, err = z.conn.Children(path)
return list, err
}
func (z *ZKServer) Exists(path string) (exist bool, err error) {
exist, _, err = z.conn.Exists(path)
return
}
//删除 cas支持
func (z *ZKServer) Del(path string) (err error) {
_, sate, _ := z.GetDataByPath(path)
fmt.Println(sate)
err = z.conn.Delete(path, sate.Version)
return err
}
3.测试
节点发现测试
package main
import (
"fmt"
"log"
"os"
"os/signal"
"syscall"
"time"
"zk/curd"
)
func main() {
driver, err := curd.NewZKServer([]string{"192.168.101.141:2181"})
if err != nil {
log.Fatal(err)
}
defer driver.Close()
// 60秒10次变动
for i := 0; i < 10; i++ {
if err = driver.RegistHostOnPath("/server", fmt.Sprintf("192.168.1.1%d", i)); err != nil {
fmt.Println("failed regist host at path")
} else {
fmt.Println("regist host successfully")
}
time.Sleep(10 * time.Second)
}
fmt.Println("节点变动已完成")
quit := make(chan os.Signal)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
fmt.Println("-------------------")
<-quit
}
package main
import (
"fmt"
"log"
"os"
"os/signal"
"strings"
"syscall"
"time"
"zk/curd"
)
func main() {
driver, err := curd.NewZKServer([]string{"192.168.101.141:2181"})
if err != nil {
log.Fatal(err)
}
defer driver.Close()
// 获取节点列表, 如果节点不存在则新建
nodeList, err := driver.GetListByPath("/server")
if err != nil && strings.Contains(err.Error(), "not exist") {
log.Fatal("首次启动请先启动注册端先把路径给创建了(持久节点)")
} else if err != nil {
log.Fatal(err)
}
fmt.Println("server node:", nodeList)
// 监听节点变化
chanNodes, chanNodesErr := driver.WatchHostsByPath("/server")
go func() {
for {
select {
case err := <-chanNodesErr:
fmt.Println("chanNodes occur error, info: ", err.Error())
case changeNodes := <-chanNodes:
fmt.Println("node list changed, new list: ", changeNodes)
default:
time.Sleep(time.Millisecond * 200)
}
}
}()
quit := make(chan os.Signal)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
fmt.Println("----------------")
<-quit
}
内容变更测试
package main
import (
"fmt"
"log"
"os"
"os/signal"
"syscall"
"time"
"zk/curd"
)
func main() {
driver, err := curd.NewZKServer([]string{"192.168.101.141:2181"})
if err != nil {
log.Fatal(err)
}
defer driver.Close()
// 60秒10次变动
for i := 0; i < 10; i++ {
conf := fmt.Sprintf("server-id=" + fmt.Sprint(i))
if err := driver.UpdateDataByPath("/my.cnf", []byte(conf), int32(i)); err != nil {
fmt.Println("update content occur err, info: ", err.Error())
} else {
fmt.Println("update content successfully, data: ", conf)
}
time.Sleep(10 * time.Second)
}
fmt.Println("内容变动已完成")
quit := make(chan os.Signal)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit
}
package main
import (
"fmt"
"log"
"os"
"os/signal"
"strings"
"syscall"
"zk/curd"
)
func main() {
driver, err := curd.NewZKServer([]string{"192.168.101.141:2181"})
if err != nil {
log.Fatal(err)
}
defer driver.Close()
//获取节点内容
zc, _, err := driver.GetDataByPath("/my.cnf")
if err != nil && strings.Contains(err.Error(), "not exist") {
log.Fatal("首次运行请先启动内容变更程序端")
} else if err != nil {
log.Fatal(err)
}
fmt.Println("get node data:")
fmt.Println(string(zc))
//动态监听节点内容
dataChan, dataErrChan := driver.WatchDataByPath("/my.cnf")
go func() {
for {
select {
case changeErr := <-dataErrChan:
fmt.Println("content change occur err, info: ", changeErr)
case changedData := <-dataChan:
fmt.Println("WatchGetData changed, info: ", string(changedData))
}
}
}()
quit := make(chan os.Signal)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit
}