Administrator
Administrator
Published on 2024-12-04 / 51 Visits
0
0

zookeeper

我只尝试了分布式锁,至于其它功能,如队列等没有尝试。

在创建包含二级路径的节点时,一级路径必须先创建

集群模式下,在创建节点时,会即时同步到其它follower(observer会稍迟)

经过测试,在不同的电脑中,连不同的IP,只要在一个集群中创建相同节点时,只有一个会成功(可以确保唯一),可以以此作为临界锁。

用sh运行程序时,日志文件在zookeeper/logs

需要JAVA JDK环境,安装方法

下载&简单配置&启动

https://zookeeper.apache.org/releases.html

在里面找到最新的稳定发布版本,下载后解压到一个文件夹里,如:D:/Soft/zookeeper3.8.4

新建一个data目录,将conf目录下的zoo_sample.cfg复制一个并重命名为zoo.cfg

extendedTypesEnabled=true #启用TTL,允许创建带过期时间的节点
dataDir=D:/Soft/zookeeper3.8.4/data #data前面的路径改为实际解压程序的路径,注意WINDOWS中路径不要用\,用/

保存zoo.cfg

(windows中)进入bin目录双击zkServer.cmd运行即可

windows中用sh启动(默认是直接运行cmd文件)

安装git for windows

进入zookeeper/bin,右键,点open git bash here

./zkServer.sh start #启动
./zkServer.sh status #查看当前状态
./zkServer.sh stop #停止

mac中启动

像上面一样修改zoo.cfg,然后在终端中进入bin目录

chmod +x zkServer.sh #给权限(如果需要)
./zkServer.sh start #启动
./zkServer.sh status #查看当前状态
./zkServer.sh stop #停止

(配置)修改Java系统属性

有些配置项只有修改启动参数,就像检查过期节点的周期

如果不修改,默认是60秒,如果设置一个节点过期时间是5秒,那么理论上最久需要65秒才被删除......

相关文档链接

以下用checkIntervalMs作为例子

znode.container.checkIntervalMs : (Java system property only) New in 3.5.1: The time interval in milliseconds for each check of candidate container and ttl nodes. Default is "60000".

翻译过来就是:每次检查候选容器和ttl节点的时间间隔(毫秒)。默认值为“60000”。

它只属于java属性,所以无法在zoo.cfg中修改

修改zkServer.cmd(windows中的运行脚本)

#用记事本打开,找到类似:
#call %JAVA% "-Dzookeeper.log.dir=%ZOO_LOG_DIR%" "-Dzookeeper.log.file=%ZOO_LOG_FILE%"
#改成,2000=2秒
call %JAVA% "-Dznode.container.checkIntervalMs=2000" "-Dzookeeper.log.dir=%ZOO_LOG_DIR%"..................................

修改zkServer.sh(Linux/bash)

#找到类似:
#    nohup "$JAVA" $ZOO_DATADIR_AUTOCREATE "-Dzookeeper.log.dir=${ZOO_LOG_DIR}" \
#    "-Dzookeeper.log.file=${ZOO_LOG_FILE}" \

改成:
    nohup "$JAVA" $ZOO_DATADIR_AUTOCREATE "-Dzookeeper.log.dir=${ZOO_LOG_DIR}" \
    "-Dznode.container.checkIntervalMs=2000" \
    "-Dzookeeper.log.file=${ZOO_LOG_FILE}" \

修改后运行,日志中看到:Using checkIntervalMs=2000 maxPerMinute=10000 maxNeverUsedIntervalMs=0说明就成功了

其它参数修改方法相同。

如果看到类似:

admin.enableServer : (Java system property: zookeeper.admin.enableServer)

这样的属性,说明即可以在zoo.cfg中配置,也可以在java参数中设置

golang中简单使用

以下代码只会有一个线程创建成功(如果无法获取锁Create方法会创建失败并立即返回错误,不会等待)

	conn, _, err := zk.Connect([]string{"127.0.0.1:2181"}, 5*time.Second)
	if err != nil {
		log.Fatalf("Failed to connect to Zookeeper: %v", err)
		return
	}
	defer conn.Close()
	path := "/JY01-ruku"
	var data = make([]byte, 0)
	for i := 0; i < 20; i++ {
		go func() {
			_, err := conn.Create(path, data, zk.FlagEphemeral, zk.WorldACL(zk.PermAll))
			if err != nil {
				fmt.Println("创建失败", i, time.Now().UnixMilli())
				return
			}
			fmt.Println("节点创建成功(持有锁)", i, time.Now().UnixMilli())
		}()
	}

其它常用方法:

	conn.Children("/")     //获取所有一级节点,如有:/test/a,/zhangsan/b,将获取到["test","zhangsan"]
	conn.Children("/test") //获取test下的所有子节点,如有:/test/a,/zhangsan/b,将获取到["a"]

	conn.Delete("/test/zhangsan", -1) //移除test下的zhangsan,test仍然会保留

    conn.Exists("/ruku/test") //节点是否存在

    //flag并用
    conn.Create("/ruku8", data, zk.FlagEphemeral | zk.FlagSequence, zk.WorldACL(zk.PermAll))

flags

const (
	FlagPersistent                  = 0  //即使连接和zk客户端关闭,创建的节点依然存在,只能通过delete移除
	FlagEphemeral                   = 1  //临时节点,在连接断开后即移除,连接不断它会一直存在,也可以通过delete移除,
	FlagSequence                    = 2  //顺序节点,会在传入的path后面加上一串唯一的数字,比如"/ceshi-",实际创建出来是/ceshi-00000000010
	FlagEphemeralSequential         = 3  //FlagEphemeral+FlagSequence
	FlagContainer                   = 4  //类似FlagPersistent,只是由它创建的一级节点下没有子节点时,会自动移除
	FlagTTL                         = 5  //带过期时间的持久节点
	FlagPersistentSequentialWithTTL = 6  //带过期时间、持久、顺序的节点
)

到达过期时间自动删除和container没有子节点自动删除不是瞬时完成的,zk内有检测线程,周期轮询去取消、删除符合条件的节点。

轮询的周期根据配置文件的checkIntervalMs来控制,默认60000毫秒一次。(但是我还没尝试出如何正确配置这一项,可能配置项有前缀之类的)

container首次添加是没有子节点的,在首次添加子节点前,它不会被自动删除,直到它至少曾经有一个子节点并且当前没有子节点,才会被移除

conn.CreateTTL("/ruku7", data, zk.FlagTTL, zk.WorldACL(zk.PermAll), time.Second*3)
conn.CreateTTL("/ruku7", data, zk.FlagPersistentSequentialWithTTL, zk.WorldACL(zk.PermAll), time.Second*3)

conn.CreateContainer("/ruku7", data, zk.FlagContainer, zk.WorldACL(zk.PermAll))

Node.js中简单使用

node-zookeeper-client模块不支持TTL,而且flags也不完整,不太好用

npm install zookeeper

代码:

const zookeeper = require('zookeeper');

var client = new zookeeper({
    connect: '127.0.0.1:2181', //集群连接方式:'127.0.0.1:2181,192.168.101.101:2181,192.168.101.102:2181'
    timeout: 5000,
    debug_level: zookeeper.constants.ZOO_LOG_LEVEL_INFO,
    host_order_deterministic: false,
})

client.connect(null, ()=>{}) //因为在new时已经传入了config这里不用传,回调函数必须传,否则报错


//使用await/async
async function test(){
    for(let i = 0; i<1; i++) {
        //创建临时节点
        // var a = await client.create("/ruku-200", "test", zookeeper.constants.ZOO_EPHEMERAL)

        //创建TTL
        var a = await client.create("/ruku-200", "test", zookeeper.constants.ZOO_PERSISTENT_WITH_TTL, 3000)
    
        //create函数签名,返回值为string类型,如果创建失败,报抛出错误,所以要在try{}中运行
        // create(path: string, data: (string | Buffer), flags: number, ttl?: number | undefined): Promise<string>;

        await new Promise(s => setTimeout(() => { s() }, 5000))
    
        //删除节点
        // var b = await client.delete_("/ruku-200", -1)
        // console.log(b);
    }
}

test()

flags在require('zookeeper').constants对象里面,与GOLANG名称相似,

在linux系统中的一波三折

安装zookeeper的npm包时就出现了错误,要求python3.6.0+(又好像是python3.10+,事后整理的可能记错)、gcc等环境

首先是安装GCC,安装GCC后npm i zookeeper时才会成功

运行时又会报错

Error: /lib64/libstdc++.so.6: version CXXABI_1.3.9' not found (required by /data/nodejs_playground/node_modules/zookeeper/build/Release/zookeeper.node)

原因是依然在使用旧的libstdc++这个动态库,无法识别到新装的GCC的lib64目录

解决方法:添加动态库路径

让系统识别到新装的GCC lib64目录后,就可以成功运行了

集群中的多种模式(角色)

  • leader:领导者,无法通过配置文件指定哪一个IP是leader,只能通过自动选举。主要管理事务日志,协调集群一致性

  • Standalone:单节点时,就是这个模式,自己是leader,也是follower

  • Observer:观察者,只同步数据,不参与投票和写数据

  • follower:同步数据,且参与投票。不需要特殊配置,所有角色默认都是follower,自动选举之后其中一台会成为leader.

集群配置

需要配置奇数台服务,比如1台、3台,偶数台无法运行,会报错:No server failure will be tolerated. You need at least 3 servers

每台服务务的dataDir目录中,新建一个文件名为myid的文件(没有后缀),内容为自己服务器的数字编号(第一台是1,第二台是2....)

每台服务器的zoo.cfg中都要添加如下信息,将IP换成自己的服务器或局域名电脑IP,2888和3888是固定的

server.1=192.168.101.105:2888:3888
server.2=192.168.101.101:2888:3888
server.3=192.168.101.102:2888:3888
#server.3=192.168.101.102:2888:3888:observer 配置当前服务器为observer

然后每台服务器启动zkServer

在golang中连接集群

	conn, _, err := zk.Connect([]string{"192.168.101.101:2181", "192.168.101.105:2181", "192.168.101.102:2181"}, 5*time.Second)
	if err != nil {
		log.Fatalf("Failed to connect to Zookeeper: %v", err)
		return
	}
	defer conn.Close()

会随机连接一个,如果正连接的节点无效(比如说未开启),会自动尝试其它节点


Comment