zookeeper笔记

目录

一、zookeeper入门

1.1 zookeeper工作机制

1.2 zookeeper特点

1.3 数据结构

1.4 应用场景

二、zookeeper操作

2.1 zookeeper服务启动等命令

2.2 客户端命令行操作

2.3 客户端 Golang API 操作

三、面试

3.1 选举机制

3.2 生产集群安装多少 zk 合适?

3.3 常用命令


一、zookeeper入门

1.1 zookeeper工作机制

        zookeeper从设计模式角度来理解:是一个基于观察者模式设计的分布式服务器管理框架,它负责存储和管理大家都关系的数据,然后接受观察者的注册,一旦这些数据的状态发生变化,zookeeper就将负责通知已经zookeeper上注册的那些观察者做出相应的反应。

        zookeeper = 文件系统 + 通知机制

1.2 zookeeper特点

        1)一个leader,多个follower组成的集群;

        2)集群中主要有半数以上节点存活,zookeeper集群就能正常服务(一般按照奇数台服务器);

        3)全局数据一致:每个server保存一份相同的数据副本,client无论连接到哪个server,数据都是一致的;

        4)更新请求顺序执行,来自同一个client的更新请求按其发送顺序依次执行;

        5)数据更新原子性:一次数据更新要么成功,要么失败;

        6)实时性:在一定时间范围内,client能读取到最新的数据。

1.3 数据结构

        zookeeper数据模型的结构与Unix文件系统很类似,整体上可以看做是一棵树,每个节点称作一个ZNode。每一个ZNode默认能够存储1MB的数据,每个ZNode都可以通过其路径唯一标识。

1.4 应用场景

        提供的服务包括:统一命名服务、统一配置管理、统一集群管理、服务器节点动态上下线、软负载均衡等。

二、zookeeper操作

2.1 zookeeper服务启动等命令

        先进入zookeeper安装目录

        1)启动zookeeper

        bin/zkServer.sh start

        2)查看状态

        bin/zkServer.sh status

        3)启动客户端

        bin/zkCli.sh

        4)退出客户端

        quit

        5)停止zookeeper

        bin/zkServer.sh stop

2.2 客户端命令行操作

        

命令基本语法 功能描述
help显示所有操作命令
ls path

使用 ls 命令来查看当前znode的子节点【可监听】

-w 监听子节点变化

-s 附加次级信息

create       

普通创建

-s 含有序列

-e 临时(重启或者超时消失)

get path

获得节点的值【可监听】

-w 监听节点内容变化

-s 附加次级信息

set 设置节点的具体值
stat查看节点状态
delete删除节点
deleteall递归删除节点

2.3 客户端 Golang API 操作

2.3.1 curd操作

package main

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

var (
	host = []string{"127.0.0.1:2181"}
)

func main() {
	conn, _, err := zk.Connect(host, 5*time.Second)
	if err != nil {
		panic(err)
	}

	//增
	if _, err := conn.Create("/test_tree2", []byte("tree_content"),
		0, zk.WorldACL(zk.PermAll)); err != nil {
		fmt.Println("create err", err)
	}

	//查
	nodeValue, dStat, err := conn.Get("/test_tree2")
	if err != nil {
		fmt.Println("get err", err)
		return
	}
	fmt.Println("nodeValue", string(nodeValue))

	//改
	if _, err := conn.Set("/test_tree2", []byte("new_content"),
		dStat.Version); err != nil {
		fmt.Println("update err", err)
	}

	//删除
	_, dStat, _ = conn.Get("/test_tree2")
	if err := conn.Delete("/test_tree2", dStat.Version); err != nil {
		fmt.Println("Delete err", err)
		//return
	}

	//验证存在
	hasNode, _, err := conn.Exists("/test_tree2")
	if err != nil {
		fmt.Println("Exists err", err)
		//return
	}
	fmt.Println("node Exist", hasNode)

	//增加
	if _, err := conn.Create("/test_tree2", []byte("tree_content"),
		0, zk.WorldACL(zk.PermAll)); err != nil {
		fmt.Println("create err", err)
	}

	//设置子节点
	if _, err := conn.Create("/test_tree2/subnode", []byte("node_content"),
		0, zk.WorldACL(zk.PermAll)); err != nil {
		fmt.Println("create err", err)
	}

	//获取子节点列表
	childNodes, _, err := conn.Children("/test_tree2")
	if err != nil {
		fmt.Println("Children err", err)
	}
	fmt.Println("childNodes", childNodes)
}

2.3.2 监听操作

        zk.go

package zookeeper

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

type ZkManager struct {
	hosts      []string
	conn       *zk.Conn
	pathPrefix string
}

func NewZkManager(hosts []string) *ZkManager {
	return &ZkManager{hosts: hosts, pathPrefix: "/gateway_servers_"}
}

//连接zk服务器
func (z *ZkManager) GetConnect() error {
	conn, _, err := zk.Connect(z.hosts, 5*time.Second)
	if err != nil {
		return err
	}
	z.conn = conn
	return nil
}

//关闭服务
func (z *ZkManager) Close() {
	z.conn.Close()
	return
}

//获取配置
func (z *ZkManager) GetPathData(nodePath string) ([]byte, *zk.Stat, error) {
	return z.conn.Get(nodePath)
}

//更新配置
func (z *ZkManager) SetPathData(nodePath string, config []byte, version int32) (err error) {
	ex, _, _ := z.conn.Exists(nodePath)
	if !ex {
		z.conn.Create(nodePath, config, 0, zk.WorldACL(zk.PermAll))
		return nil
	}
	_, dStat, err := z.GetPathData(nodePath)
	if err != nil {
		return
	}
	_, err = z.conn.Set(nodePath, config, dStat.Version)
	if err != nil {
		fmt.Println("Update node error", err)
		return err
	}
	fmt.Println("SetData ok")
	return
}

//创建临时节点
func (z *ZkManager) RegistServerPath(nodePath, host string) (err error) {
	ex, _, err := z.conn.Exists(nodePath)
	if err != nil {
		fmt.Println("Exists error", nodePath)
		return err
	}
	if !ex {
		//持久化节点,思考题:如果不是持久化节点会怎么样?
		_, err = z.conn.Create(nodePath, nil, 0, zk.WorldACL(zk.PermAll))
		if err != nil {
			fmt.Println("Create error", nodePath)
			return err
		}
	}
	//临时节点
	subNodePath := nodePath + "/" + host
	ex, _, err = z.conn.Exists(subNodePath)
	if err != nil {
		fmt.Println("Exists error", subNodePath)
		return err
	}
	if !ex {
		_, err = z.conn.Create(subNodePath, nil, zk.FlagEphemeral, zk.WorldACL(zk.PermAll))
		if err != nil {
			fmt.Println("Create error", subNodePath)
			return err
		}
	}
	return
}

//获取服务列表
func (z *ZkManager) GetServerListByPath(path string) (list []string, err error) {
	list, _, err = z.conn.Children(path)
	return
}

//watch机制,服务器有断开或者重连,收到消息
func (z *ZkManager) WatchServerListByPath(path string) (chan []string, chan error) {
	conn := z.conn
	snapshots := make(chan []string)
	errors := make(chan error)
	go func() {
		for {
			snapshot, _, events, err := conn.ChildrenW(path)
			if err != nil {
				errors <- err
			}
			snapshots <- snapshot
			select {
			case evt := <-events:
				if evt.Err != nil {
					errors <- evt.Err
				}
				fmt.Printf("ChildrenW Event Path:%v, Type:%v\n", evt.Path, evt.Type)
			}
		}
	}()

	return snapshots, errors
}

//watch机制,监听节点值变化
func (z *ZkManager) WatchPathData(nodePath string) (chan []byte, chan error) {
	conn := z.conn
	snapshots := make(chan []byte)
	errors := make(chan error)

	go func() {
		for {
			dataBuf, _, events, err := conn.GetW(nodePath)
			if err != nil {
				errors <- err
				return
			}
			snapshots <- dataBuf
			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
}

        main.go

package main

import (
	"fmt"
	"log"
	"os"
	"os/signal"
	"syscall"
)

var addr = "127.0.0.1:2002"

func main() {
	//获取zk节点列表
	zkManager := zookeeper.NewZkManager([]string{"127.0.0.1:2181"})
	zkManager.GetConnect()
	defer zkManager.Close()

	zlist, err := zkManager.GetServerListByPath("/real_server")
	fmt.Println("server node:")
	fmt.Println(zlist)
	if err != nil {
		log.Println(err)
	}

	//动态监听节点变化
	chanList, chanErr := zkManager.WatchServerListByPath("/real_server")
	go func() {
		for {
			select {
			case changeErr := <-chanErr:
				fmt.Println("changeErr")
				fmt.Println(changeErr)
			case changedList := <-chanList:
				fmt.Println("watch node changed")
				fmt.Println(changedList)
			}
		}
	}()

	//关闭信号监听
	quit := make(chan os.Signal)
	signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
	<-quit
}

三、面试

3.1 选举机制

        半数机制,超过半数的投票通过,即通过。
        1)第一次启动选举规则:
        投票过半数时,服务器 id 大的胜出
        2)第二次启动选举规则:
        ①EPOCH 大的直接胜出
        ②EPOCH 相同,事务 id 大的胜出
        ③事务 id 相同,服务器 id 大的胜出

EPOCH:每个Leader任期的代号。没有Leader时同一轮投票过程中的逻辑时钟值是相同的。每投完一次票这个数就会增加。

事务id:用来标识一次服务器状态的变更。在某一时刻,集群中的每台机器的事务id不一定完全一致,这和zookeeper服务器对于客户端“更新请求”的处理逻辑有关。

服务器id:用来唯一标识一台zookeeper集群中的机器,每台机器不能重复,和myid一致。

3.2 生产集群安装多少 zk 合适?

        安装奇数台
        生产经验:
        10 台服务器:3 台 zk;
        20 台服务器:5 台 zk;
        100 台服务器:11 台 zk;
        200 台服务器:11 台 zk
        服务器台数多:好处,提高可靠性;坏处:提高通信延时

3.3 常用命令

        ls、get、create、delete

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值