Golang基于Zookeeper实现节点注册发现、内容配置变更,发布/订阅

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
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值