Zookeeper 客户端Curator (分布式锁删除ZNode节点)

Zookeeper=文件系统+通知机制

Curator是Netflix公司开源的一套zookeeper客户端框架,解决了很多Zookeeper客户端非常底层的细节开发工作,包括连接重连、反复注册Watcher和NodeExistsException异常等等。Patrixck Hunt(Zookeeper)以一句“Guava is to Java that Curator to Zookeeper”给Curator予高度评价。

Curator包含了几个包:

  • curator-framework:对zookeeper的底层api的一些封装。
  • curator-client:提供一些客户端的操作,例如重试策略等。
  • curator-recipes:封装了一些高级特性,如:Cache事件监听、选举、分布式锁、分布式计数器、分布式Barrier等

ZooKeeper 分布式锁实现 后有些问题 需要删除ZNode节点的空文件夹

进程需要访问共享数据时, 就在"/locks"节点下创建一个sequence类型的子节点, 称为thisPath. 当thisPath在所有子节点中最小时, 说明该进程获得了锁. 进程获得锁之后, 就可以访问共享资源了

数据结构

ZooKeeper数据模型的结构与Unix文件系统很类似,整体上可以看作是一棵树,每个节点称做一个ZNode。

 很显然zookeeper集群自身维护了一套数据结构。这个存储结构是一个树形结构,其上的每一个节点,我们称之为"znode",每一个znode默认能够存储1MB的数据,每个ZNode都可以通过其路径唯一标识,leaf 称为叶子节点 如图所示

现在的项目中使用  来生成全局的唯一ID和对象锁

现在的问题是znode的存储有限制,当容量大于1MB后容易造成zookeeper宕机,所以需要删除节点数据

删除数据节点

删除一个节点

client.delete().forPath("path");

注意,此方法只能删除叶子节点,否则会抛出异常。

删除一个节点,并且递归删除其所有的子节点

client.delete().deletingChildrenIfNeeded().forPath("path");

删除一个节点,强制指定版本进行删除

client.delete().withVersion(10086).forPath("path");

删除一个节点,强制保证删除

client.delete().guaranteed().forPath("path");

guaranteed()接口是一个保障措施,只要客户端会话有效,那么Curator会在后台持续进行删除操作,直到删除节点成功。

注意:上面的多个流式接口是可以自由组合的,例如:

client.delete().guaranteed().deletingChildrenIfNeeded().withVersion(10086).forPath("path");

读取数据节点数据

读取一个节点的数据内容

client.getData().forPath("path");

注意,此方法返的返回值是byte[ ];

读取一个节点的数据内容,同时获取到该节点的stat

Stat stat = new Stat();
client.getData().storingStatIn(stat).forPath("path");

更新数据节点数据

更新一个节点的数据内容

client.setData().forPath("path","data".getBytes());

注意:该接口会返回一个Stat实例

更新一个节点的数据内容,强制指定版本进行更新

client.setData().withVersion(10086).forPath("path","data".getBytes());

检查节点是否存在

client.checkExists().forPath("path");

注意:该方法返回一个Stat实例,用于检查ZNode是否存在的操作. 可以调用额外的方法(监控或者后台处理)
并在最后调用forPath()指定要操作的ZNode获取某个节点的所有子节点路径

client.getChildren().forPath("path");

注意:该方法的返回值为List,获得ZNode的子节点Path列表。 可以调用额外的方法(监控、后台处理或者获
取状态watch, background or get stat) 并在最后调用forPath()指定要操作的父ZNode
TestController
@RestController
@Slf4j
@Api(value="test", description="测试")
public class TestController {

	private final CuratorFramework curator;
	private final LockNodeCleanService lockNodeCleanService;



	public TestController(CuratorFramework curator,LockNodeCleanService lockNodeCleanService) {
		this.curator = curator;
		this.lockNodeCleanService =lockNodeCleanService;
	}


	@GetMapping("/internal/zookeeper/listData")
	public void listData(@RequestParam String zkPath) throws Exception {
//		RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);

//		Map<String, Long> result = new HashMap<>();
		for (String string : curator.getChildren().forPath(zkPath)) {
			String tempPath = zkPath + "/" + string;
			log.info("***********:"+tempPath);
		}
	}

	@GetMapping("/internal/zookeeper/remove")
	public void listData() throws Exception {
		lockNodeCleanService.deleteEmptyLockNodes();
	}

}
LockNodeCleanService

import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.leader.LeaderLatch;
import org.apache.curator.framework.recipes.locks.InterProcessMutex;
import org.apache.curator.retry.RetryNTimes;
import org.apache.zookeeper.data.Stat;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
@Slf4j
@AllArgsConstructor
public class LockNodeCleanService {
    private final CuratorFramework curator;
    private final LeaderLatch leaderLatch;
    private static final String root = "/locks/test";


    public static void main(String[] args) throws Exception {
        CuratorFramework curator = curator(args[0]);
        InterProcessMutex lock = new InterProcessMutex(curator, "");
        lock.acquire();
        lock.release();
        LockNodeCleanService service = new LockNodeCleanService(curator, null);
        service.deleteEmptyLockNodes();
    }

    private static CuratorFramework curator(String zkAddress) {
        CuratorFramework curator = CuratorFrameworkFactory.
                builder().
                connectString(zkAddress).
                sessionTimeoutMs(1000).
                retryPolicy(new RetryNTimes(3, 1000)).
                build();
        curator.start();
        return curator;
    }

    /**
     * 删除所有空的节点
     */
    @Scheduled(cron = "0 0 4 * * ?")
    public void deleteEmptyLockNodes() {
        if (!leaderLatch.hasLeadership()) {
            log.info("deleteEmptyLockNodes not leader");
            return;
        }
        try {
            //二级目录是否存在
            Stat stat = curator.checkExists().forPath(root);
            if (stat == null) {
                return;
            }

            List<String> folders = curator.getChildren().forPath(root);
            if (CollectionUtils.isEmpty(folders)) {
                return;
            }
            for (String folder : folders) {
                //三级路径
                String folderFullPath = root + "/" + folder;
                try {
                    clearOneFolder(folderFullPath);
                } catch (Exception e) {
                    log.info("error when clear folder: {}", folderFullPath, e);
                }
            }
        } catch (Exception e) {
            log.error("x", e);

        }
    }


    /**
     * 删除一个文件夹下的空节点
     * 一个文件夹就是一种业务对象
     *
     * @param folder
     * @throws Exception
     */
    private void clearOneFolder(String folder) throws Exception {
        for (String nodePath : curator.getChildren().forPath(folder)) {
            //四级路径
            String nodeFullPath = folder + "/" + nodePath;
            List<String> folders = curator.getChildren().forPath(nodeFullPath);
            if (CollectionUtils.isEmpty(folders)) {
                try {
                    //没有子目录直接删除
                    deleteNode(nodeFullPath, curator);
                } catch (Exception e) {
                    log.info("fail to delete: {}", nodeFullPath, e);
                }
                continue;
            }
            for (String path : folders) {
                //删除第五级目录
                String pathFive = nodeFullPath + "/" + path;
                try {
                    deleteNode(pathFive, curator);
                } catch (Exception e) {
                    log.info("fail to delete: {}", pathFive, e);
                }
            }
            try {//删除第四级目录
                deleteNode(nodeFullPath, curator);
            } catch (Exception e) {
                log.info("fail to delete: {}", nodeFullPath, e);
            }




        }
    }


    /**
     * 安全删除由于锁造成的空节点
     * 确保节点存在、没有子节点、没有数据、最近修改时间在一小时前
     *
     * @param path
     * @param curator
     * @throws Exception
     */
    public static void deleteNode(String path, CuratorFramework curator) throws Exception {
        Stat stat = curator.checkExists().forPath(path);
        if (stat == null) {
            log.info(path + "  not exist");
            return;
        }
        if (stat.getNumChildren() > 0) {
            log.info(path + "  has children");
            return;
        }
        if (curator.getData().forPath(path).length > 0) {
            log.info(path + "  has data");
            return;
        }
        if (System.currentTimeMillis() - stat.getMtime() < 3600 * 1000) {
            log.info(path + "  very new");
            return;
        }
        curator.delete().inBackground().forPath(path);
        log.info(path + "  is deleted");
    }

}

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

javafanwk

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值