基于zookeeper和quartz实现分布式定时调度(二)

最近一直在学习,偶然间翻看了一下博客,发现很多人问我关于基于zookeeper和quartz实现分布式定时调度文章的一些问题。由于本人最近几年都没怎么关注博客,因此也没有做出回复,心里感觉愧对大家。所以抽时间把之前写的东西重新翻了下版。顺便把代码也分享给大家。Quartz相关的知识和类之间的关系,可以参考我之前的一篇文章。

本文对zookeeper的分布式协作相关的代码做了改进。本文中使用临时顺序节点来控制多个定时任务同时执行时,保证只有一个定时任务执行成功,其他的定时任务都不执行。实现的原理就是每个定时任务的客户端都去创建节点,谁是节点的第一位,谁执行,就是多个客户端竞争资源的思想。话不多说,想了解具体实现的同事,自己去下载代码即可,代码很简单,大家可以自己研究一下。

下载地址:https://gitee.com/alex211/ets-schedule

使用zookeeper的临时顺序节点创建实现竞争资源,代码如下:

package com.ets.schedule.zklock;

import com.ets.schedule.quartz.contants.ETSConstant;
import org.I0Itec.zkclient.ZkClient;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.InitializingBean;

import java.util.Collections;
import java.util.List;

/**
 * @author: alex
 * @Date: 2019/4/8
 * @Description: 利用zookeeper的临时顺序节点来实现协调多个quartz客户端在同一时刻保证只有一个实例去执行任务。
 * 与分布式锁的思想类似但不是一个完整的分布式锁实现。
 * 场景描述:
 * 1、多个客户端,都去创建临时节点,谁是在节点的第一位,谁获得锁,可以执行定时任务。
 * 2、一旦有客户端宕机,立即断开连接,删除该客户端创建的临时节点,让其他的客户端去执行任务。
 */
public class ZookeeperLock implements InitializingBean {

    protected static Logger logger = Logger.getLogger(ZookeeperLock.class);

    /**
     * zookeeper服务地址
     */
    private String hosts;

    /**
     * 会话超时时间(越短越好,因为服务器一断开,就删除临时节点,可以让其他存活的机器去执行任务)
     */
    private int sessionTimeout = 1000;

    /**
     * 连接超时时间
     */
    private int connectionTimeout = 1000*60;

    /**
     * zk客户端
     */
    private ZkClient client;

    /**
     * 当前节点
     */
    private ThreadLocal<String> currentPath;

    /**
     * spring 初始化后执行
     * @throws Exception
     */
    public void afterPropertiesSet() throws Exception {
        this.currentPath = new ThreadLocal<>();
        this.client = new ZkClient(hosts,sessionTimeout,connectionTimeout);  //获得客户端
        this.client.setZkSerializer(new MyZkSerializer()); //设置序列化类
        //判断根节点是否存在,不存在则创建
        if (!this.client.exists(ETSConstant.LOCKPATH)) {
            try {
                this.client.createPersistent(ETSConstant.LOCKPATH);
            } catch (Exception e) {
                logger.error("ZkClient create root node failed...");
                logger.error(e);
            }
        }
    }

    /**
     * 尝试获取锁
     * @return true 拿到,false没拿到
     */
    public boolean tryLock() {
        //当前节点为空,说明还没有线程来创建节点
        if(this.currentPath.get() == null) {
            this.currentPath.set(this.client.createEphemeralSequential(ETSConstant.LOCKPATH + ETSConstant.SEPARATOR,"data"));
        }
        // 获取所有节点
        List<String> children = this.client.getChildren(ETSConstant.LOCKPATH);
        // 排序
        Collections.sort(children);
        //判断当前节点是否是最小节点
        if(this.currentPath.get().equals(ETSConstant.LOCKPATH + ETSConstant.SEPARATOR + children.get(0))) {
            return true;
        }
        return false;
    }

    /**
     * 释放锁
     */
    public void unlock() {
        this.client.delete(this.currentPath.get());
        this.currentPath.remove();
    }

    public String getHosts() {
        return hosts;
    }

    public void setHosts(String hosts) {
        this.hosts = hosts;
    }

    public long getSessionTimeout() {
        return sessionTimeout;
    }

    public void setSessionTimeout(int sessionTimeout) {
        this.sessionTimeout = sessionTimeout;
    }

    public int getConnectionTimeout() {
        return connectionTimeout;
    }

    public void setConnectionTimeout(int connectionTimeout) {
        this.connectionTimeout = connectionTimeout;
    }
}

zookeeper的序列化类

package com.ets.schedule.zklock;

import org.I0Itec.zkclient.exception.ZkMarshallingError;
import org.I0Itec.zkclient.serialize.ZkSerializer;
import org.apache.log4j.Logger;

import java.io.UnsupportedEncodingException;

/**
 * @author: alex
 * @Date: 2019/4/8
 * @Description: 序列化
 */
public class MyZkSerializer implements ZkSerializer {

    protected static Logger logger = Logger.getLogger(MyZkSerializer.class);

    /**
     * 反序列化
     * @param bytes 字节数组
     * @return 实体
     * @throws ZkMarshallingError
     */
    public Object deserialize(byte[] bytes) throws ZkMarshallingError {
        try {
            return new String(bytes, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            logger.error("MyZkSerializer deserialize happened unsupportedEncodingException...");
            logger.error(e);
            throw new ZkMarshallingError(e);
        }
    }

    /**
     * 序列化
     * @param obj 实体
     * @return 字节数组
     * @throws ZkMarshallingError
     */
    public byte[] serialize(Object obj) throws ZkMarshallingError {
        try {
            return String.valueOf(obj).getBytes("UTF-8");
        } catch (UnsupportedEncodingException e) {
            logger.error("MyZkSerializer serialize happened unsupportedEncodingException...");
            logger.error(e);
            throw new ZkMarshallingError(e);
        }
    }
}

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值