利用zookeeper实现分布式锁

package main

import (
	"time"
	"github.com/samuel/go-zookeeper/zk"
	"errors"
	"strings"
	"strconv"
	"fmt"
	"sync"
)

const (
	Pre_Child_Node = "child_"
	Default_Version = 0
	Root_Node = "/lock"
	)

func main()  {
	var hosts = []string{"localhost:2181"}
	conn,_,err := zk.Connect(hosts,time.Second*60)
	if err != nil{
		return
	}
	zkServer := ZKServer{Conn:conn,Nodes:make(map[int]Node)}
	_,err = zkServer.Conn.Create(Root_Node,[]byte(""),zk.FlagSequence,zk.WorldACL(zk.PermAll))

	children,_,_:= zkServer.Conn.Children(Root_Node)
	for _,childNode := range children{
		zkServer.Conn.Delete(fmt.Sprintf("%s/%s",Root_Node,childNode),Default_Version)
	}

	lock := DistributeLock{ZKServer:&zkServer}
	var waitGroutp = sync.WaitGroup{}
	//循环创建子节点
	for i:=0;i<10;i++{
		waitGroutp.Add(1)
		go func(i int) {
			err = lock.Acquire()
			children,_,_= zkServer.Conn.Children(Root_Node)
			fmt.Println(err)
			fmt.Println(fmt.Sprintf("goroute_%d",i))
			fmt.Println(fmt.Sprintf("done:%d",i))
			lock.Release()
			waitGroutp.Done()
		}(i)
	}
	waitGroutp.Wait()
	fmt.Println("end...")
}

type DistributeLock struct{
	ZKServer *ZKServer
	NodeNum int
}

//获取锁,没获取到一直阻塞
func (dLock *DistributeLock)Acquire()(error){
	zkServer := dLock.ZKServer
	node,err := dLock.ZKServer.CreateNode(fmt.Sprintf("%s/%s",Root_Node,Pre_Child_Node),"",zk.FlagSequence)
	ChildNodeNum := zkServer.GetChildNodeNumByName(node.Path)
	minChildNodeNum,err := zkServer.GetMinChildNum(Root_Node)
	if err != nil{
		return err
	}
	haveGetLock := false
	for{
		if ChildNodeNum == minChildNodeNum{
			haveGetLock = true
		}
		if haveGetLock {
			fmt.Println("777")
			break
		}

		lastNodeNum ,_:= zkServer.GetLastSortChildNode(ChildNodeNum)
		isExist,_,event_ch,_:= zkServer.Conn.ExistsW(zkServer.GetNodePathByNum(lastNodeNum))
		if !isExist{
			break
		}
		event := <- event_ch
		if event.Type == zk.EventNodeDeleted{
			//上一个节点删除则触发获得锁
			haveGetLock = true
		}
	}
	dLock.NodeNum = ChildNodeNum
	return nil
}

//释放锁
func (dLock *DistributeLock)Release()error{
	nodePath:= dLock.ZKServer.GetNodePathByNum(dLock.NodeNum)
	isExist,_,err := dLock.ZKServer.Conn.Exists(nodePath)
	if err != nil{
		return err
	}
	if isExist{
		err = dLock.ZKServer.Conn.Delete(nodePath,Default_Version)
	}
	return err
}

type ZKServer struct {
	Conn *zk.Conn
	Nodes map[int]Node
	Mtx sync.Mutex
}
//获取最小子节点
func (zkServer *ZKServer)GetMinChildNum(path string)(nodeNum int,err error){
	childNodeNums ,err := zkServer.GetAllChildrenNum(path)
	if err != nil{
		return 0,err
	}

	if len(childNodeNums)<=0{
		return 0,nil
	}

	minChildNum := childNodeNums[0]
	for _,childNodeNum :=range childNodeNums{
		if childNodeNum<minChildNum {
			minChildNum = childNodeNum
		}
	}

	return minChildNum,nil
}

//获取所有子节点编号
func (zkServer * ZKServer) GetAllChildrenNum(path string)([]int,error){
	if zkServer==nil && zkServer.Conn==nil{
		return nil,errors.New("fail to get zk conn")
	}
	childPaths,_,err := zkServer.Conn.Children(path)
	if err != nil{
		return nil,err
	}

	childNodeNums := make([]int,0)
	for _,childPath:=range childPaths{
		segmentPaths := strings.Split(childPath,Pre_Child_Node)
		if len(segmentPaths)!=2{
			return nil,errors.New("path is not correct")
		}
		childNodeNum ,_:= strconv.Atoi(segmentPaths[1])
		childNodeNums = append(childNodeNums,childNodeNum)
	}
	return childNodeNums,nil
}

type Node struct {
	Num int
	Path string
}

//创建节点
func (zkServer *ZKServer)CreateNode(path string,data string,nodeType int)(*Node,error){
	if zkServer==nil && zkServer.Conn==nil{
		return  nil,errors.New("fail to get zk conn")
	}
	name,err := zkServer.Conn.Create(path,[]byte(data),int32(nodeType),zk.WorldACL(zk.PermAll))
	num := zkServer.GetChildNodeNumByName(name)
	node := Node{
		Num:zkServer.GetChildNodeNumByName(name),
		Path:name,
	}
	zkServer.Mtx.Lock()
	zkServer.Nodes[num] = node
	zkServer.Mtx.Unlock()
   return &node,err
}

//删除节点
func(zkServer *ZKServer)DeleteNode(node *Node)error{
	if zkServer==nil && zkServer.Conn==nil{
		return errors.New("fail to get zk conn")
	}
	err := zkServer.Conn.Delete(node.Path,Default_Version)
	fmt.Println(err)
	return err
}

//根据根据名字获取节点编号
func (zkServer *ZKServer)GetChildNodeNumByName(nodeName string)int{
	segmentPaths := strings.Split(nodeName,Pre_Child_Node)
	if len(segmentPaths)!=2{
		return 0
	}
	childNodeNum ,_:= strconv.Atoi(segmentPaths[1])
	return childNodeNum
}

//获取上一个顺序节点
func (zkServer *ZKServer)GetLastSortChildNode(nodeNum int)(int,error){
	childNodeNums ,err:= zkServer.GetAllChildrenNum(Root_Node)
	if err != nil{
		return 0,err
	}

	allLessNodes := make([]int,0) //所有序号大于nodeNum节点序号
	for _,childNodeNum := range childNodeNums{
		if childNodeNum<nodeNum{
			allLessNodes = append(allLessNodes,childNodeNum)
		}
	}

	if len(allLessNodes)<=0{
		return 0,errors.New("get fail")
	}
	lastNodeNum := allLessNodes[0]
	for _,nodeNum:= range allLessNodes{
		if nodeNum>lastNodeNum{
			lastNodeNum = nodeNum
		}
	}
	return lastNodeNum,nil
}

func (zkServer *ZKServer) GetNodePathByNum( nodeNum int)string{
	node := zkServer.Nodes[nodeNum]
	return node.Path
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值