zookeeper

1.zookeeper出现的历史及其原因

Zookeeper 最早起源于雅虎研究院的一个研究小组。在当时,研究人员发现,在雅虎内部很多大型系统基本都需要依赖一个类似的系统来进行分布式协调,但是这些系统往往都存在分布式单点问题。

所以,雅虎的开发人员就试图开发一个通用的无单点问题的分布式协调框架,以便让开发人员将精力集中在处理业务逻辑上。

 

2.zookeeper的作用

(1)注册中心

(2)分布式锁

(3)负载均衡,集群扩展等

......

 

3.数据模型

zookeeper

zookeeper

可以看出来非常向文件系统,每个节点即是目录,也是文件,里面包含数据data,stat:描述该znode的版本权限等信息,  children:该znode下的子节点

stat:

zookeeper

其中:有个属性numChildren,这个后面会用到,也就是把子节点数作为节点重入数

 

4.命令

zookeeper

 

5.特性

(1)节点只能一级一级创建

  create /dir1/dir2 data :如果dir1节点不存在会报错

 (2)同级节点的唯一性(可用来实现分布式锁)

     如果已经存在节点/dir1/dir2,在创建/dir1/dir2会报错,节点已存在

    这个zookeeper实现同级节点唯一性的方案是lock(父亲节点)

(3)可以创建临时节点(可用来实现分布式锁,而且能避免死锁,解决服务宕机等问题)

create -e /dir1/dir2 value

与客户端会话绑定,一旦客户端会话失效,这个客户端所创建的所有临时节点都会被移除

(4)可以创建顺序节点

   create -s /dir1/dir2 value  :可创建节点/dir1/dir20000000001

  create -s /dir1/        value :可创建节点/dir1/000000001

 

5.支持发布订阅机制(Watch)

Semantics of Watches

We can set watches with the three calls that read the state of ZooKeeper: exists, getData, and getChildren. The following list details the events that a watch can trigger and the calls that enable them:

  • Created event: Enabled with a call to exists.
  • Deleted event: Enabled with a call to exists, getData, and getChildren.
  • Changed event: Enabled with a call to exists and getData.
  • Child event: Enabled with a call to getChildren.

我们可以通过三个调用来读取ZooKeeper状态的调用来设置监控:exist,getData和getChildren。 以下列表详细说明了手表可以触发的事件以及启用它们的调用:

已创建的事件:已启用并调用了存在。
已删除事件:启用并调用了exist,getData和getChildren。
更改的事件:启用并调用存在和getData。
子事件:通过调用getChildren启用。

 

比如说客户端调用exist("/dir1/dir2") 命令并设置watch,当/dir1/dir2节点被删除的时候,zk 服务端会回调客户端,告诉客户端该节点被删除

zk_client.exists(node_path, call_back)

def call_back(event: WatchedEvent):
    if event.type == EventType.DELETED:
        return True
 

6.利用zookeeper同级节点不能重名+临时节点+watch 实现分布式锁

(1)方案1

命名空间为(基础目录):/lock

获取锁A : create -e /lock/A  成功则获取锁,节点已存在则失败

获取锁A,自旋+把锁A当做临时节点+watch(可不用) 实现尝试一定时间非阻塞锁

示例:

func Test3Query(t *testing.T) {
	basePath := "/testplan/"
	node := "dir1"
	nodePath := basePath + node
	conn, _, err := zk.Connect([]string{"localhost:2181"}, 2*time.Second)
	if err != nil {
		fmt.Println(err.Error())
	}
	tryGetLock(conn, nodePath)

}

func tryGetLock(conn *zk.Conn, nodePath string) (bool, error) {
	startTime := time.Now()
	exist, _, event, _ := conn.ExistsW(nodePath)
	if !exist {
		res, _ := getNodeLock(conn, nodePath)
		if res {
			return true, nil
		}
	}
	for {
		if time.Now().Sub(startTime).Seconds() >= 2 {
			return false, nil
		}
		select {
		case ev := <-event:
			if ev.State == zk.State(zk.EventNodeDeleted) {

			} else {
				return false, fmt.Errorf("不可预知的事件")
			}
		}
	}

}

func getNodeLock(conn *zk.Conn, nodePath string) (bool, error) {
	_, err := conn.Create(nodePath, nil, zk.FlagEphemeral, nil)
	if err == nil {
		return true, nil
	}
	if err == zk.ErrNodeExists {
		return false, nil
	}
	return false, err
}

 

(2)方案2 利用临时顺序节点+watch实现

https://blog.csdn.net/kongmin_123/article/details/82081953

 

(3)利用zookeeper实现读写锁

https://www.pianshen.com/article/1602134514/

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

上一篇:Linux实验一


下一篇:分布式目录path锁实现方案