使用zookeeper一个简单分布式demo

zookeeper是一个提供分布式程序协调服务的应用,它的命名空间类似linux系统文件路径等等。 具体描述可以参考官网 https://zookeeper.apache.org/doc/trunk/zookeeperOver.html

本文将描述如何借助zookeeper构建一个简单的任务发布运行应用,使用curator。(我也是刚接触zk,如有不对的地方希望指出.谢谢)

设计

我将任务发布部分作为server,任务处理部分作为client

server部分

server部分功能比较简单。主要进行任务的发布。
首先server启动时向zk创建节点/test ,为持久节点
1

之后我们向server提交任务,假设任务名称为localtask,数据为hello worldserver会向zk/test节点下注册新的任务节点,这是一个持久节点,以任务名作为节点名,任务的数据存放在节点内。
2

client部分

client部分主要接收任务,并允许任务。同时一旦某个运行任务所在的机器挂掉,其它机器能够接管该任务。下面以一个client为例来介绍。

client启动时会向zk添加对/test节点的listener
3

server发布任务后,client能够监听任务节点的创建
4

client发现新任务时,首先会对该任务节点添加子节点变化的listener,之后会尝试对任务加锁,加锁成功的客户端能够运行任务。
如何进行加锁: 创建任务节点的子节点/lock,这是一个临时节点。 因为zookeeper会保证多个客户端同时创建同一个节点,只有一客户端能够创建成功。当某台机器宕机,那么就与zk失去了心跳,那么该临时节点就会被删除,那么其它节点会尝试去加锁。
5

如果/lock节点创建成功,则表示当前客户端获得了运行任务的权利。客户端开始启动任务

6

代码实现

这里只给出部分代码,全部代码
https://github.com/sweat123/distribute_cluster

server实现

创建任务根节点

    public void start() {
        if (!nodeExists(ROOT)) {
            try {
                zkClient.create().creatingParentsIfNeeded().withMode(PERSISTENT).forPath(ROOT);
            } catch (Exception e) {
                LOG.error("create root failed.", e);
            }
        }
    }

创建任务

    public void createTask(String taskName, byte[] data) {
        String path = ROOT + "/" + taskName;
        if (nodeExists(path)) {
            LOG.error("task already exists.");
            return;
        }
        try {
            zkClient.create().withMode(PERSISTENT).forPath(path, data);
        } catch (Exception e) {
            LOG.error("create task failed. node path: {}", path, e);
        }
    }

client实现

添加对任务根节点的listener

    public void registry() throws Exception {
        PathChildrenCache childrenCache = new PathChildrenCache(zkClient, ROOT, true);
        childrenCache.start();
        childrenCache.getListenable().addListener((client, event) -> {
            switch (event.getType()) {
            case CHILD_ADDED:
                LOG.info("child add; node path: {}", event.getData().getPath());
                lockAndWork(client, event);
                addLockListener(client, event.getData().getPath());
                break;
            case CHILD_REMOVED:
                LOG.info("child removed; node path: {}", event.getData().getPath());
                break;
            }
        });
    }

server任务发布后,创建对任务节点的listener,并尝试对任务加锁,如果加锁成功则运行任务

    private boolean tryLock(CuratorFramework zkClient, String path) {
        try {
            zkClient.create().withMode(EPHEMERAL).forPath(path);
        } catch (Exception e) {
            LOG.error("create node failed; node path: {}", path, e);
            return false;
        }
        return true;
    }
    
    private void lockAndWork(CuratorFramework zkClient, PathChildrenCacheEvent event) {
        String path = event.getData().getPath();
        byte[] nodeData = event.getData().getData();
        if (!tryLock(zkClient, path + "/lock")) {
            LOG.info("lock node failed.");
            return;
        }
        //lock succeed
        createTask(nodeData);
    }

    private void createTask(byte[] data) {
        String dataStr = Utils.convertJsonByteArrToAssignedObj(data, "key", String.class);
        LOG.info("create task with data {}", dataStr);
    }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要实现分布式并发代码而不使用ZooKeeper这样的分布式协调服务,可以考虑使用Redis等内存数据库或消息队列来实现分布式锁或任务调度。下面是一个示例,使用Redis实现分布式并发代码: ```java import redis.clients.jedis.Jedis; import redis.clients.jedis.params.SetParams; public class DistributedConcurrencyExample { private static final String LOCK_KEY = "my_lock"; private static final int LOCK_EXPIRE_TIME = 5000; // 锁的过期时间,单位为毫秒 public static void main(String[] args) { Thread t1 = new Thread(new JobTask()); Thread t2 = new Thread(new JobTask()); t1.start(); t2.start(); } static class JobTask implements Runnable { @Override public void run() { Jedis jedis = new Jedis("localhost", 6379); // 尝试获取分布式锁 boolean acquired = false; try { while (!acquired) { // 设置锁的过期时间,防止锁被长时间占用 SetParams params = SetParams.setParams().nx().px(LOCK_EXPIRE_TIME); String result = jedis.set(LOCK_KEY, "locked", params); if ("OK".equals(result)) { acquired = true; System.out.println(Thread.currentThread().getName() + " acquired the lock."); // 执行任务 executeJob(); // 释放锁 jedis.del(LOCK_KEY); System.out.println(Thread.currentThread().getName() + " released the lock."); } else { // 未获取到锁,等待一段时间后重试 Thread.sleep(1000); } } } catch (InterruptedException e) { e.printStackTrace(); } finally { jedis.close(); } } private void executeJob() { // 任务执行逻辑 System.out.println(Thread.currentThread().getName() + " is executing the job."); } } } ``` 在这个示例中,我们使用Redis作为分布式锁的存储介质。每个线程在执行任务之前,会尝试获取名为`my_lock`的分布式锁。 在获取锁时,我们使用`set`命令设置锁的过期时间,并指定了`NX`参数来确保只有一个线程能够成功设置锁。如果获取锁成功,线程执行任务并在任务完成后释放锁。 如果获取锁失败,线程会等待一段时间后重新尝试获取锁。这里使用简单的等待策略,每次等待1秒后再次尝试获取锁。你可以根据实际需求和性能要求调整等待策略。 需要注意的是,这个示例只是一个简单的演示,并没有处理分布式场景中的各种异常和边界情况。在实际开发中,需要更加细致地处理分布式锁的竞争条件、故障恢复、死锁避免等问题。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值