使用zookeeper搭建分布式定时任务可执行锁

浪尽此生 提交于 2020-03-19 14:59:49

3 月,跳不动了?>>>

    最近接手一个含有定时任务的历史项目,需要部署两个实例,为了减少重复打包的工作,在技术经理的建议下,决定采用zookeeper临时节点存在与否的方式来控制各个实例下定时任务的是否执行,同时也大部分避免了宕机的情况定时任务不会执行的情况。

    具体代码实现:

import com.loan_manage.service.MasterService;
import com.netflix.curator.RetryPolicy;
import com.netflix.curator.framework.CuratorFramework;
import com.netflix.curator.framework.CuratorFrameworkFactory;
import com.netflix.curator.retry.RetryUntilElapsed;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.data.Stat;
import org.junit.Assert;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.util.List;

/**
 * 是否主节点实现
 * @author wuhanhong
 */
@Service
public class MasterServiceImpl implements MasterService {

    private final static Logger logger = LoggerFactory.getLogger(MasterServiceImpl.class);
    private CuratorFramework zk;

    @Value("${zookeeper.registry.address}")
    private String hosts;
    @Value("${zookeeper.retry.times}")
    private Integer retry;
    @Value("${zookeeper.connection.timeout}")
    private Integer connectTimeout;
    @Value(("${zookeeper.session.timeout}"))
    private Integer sessionTimeout;
    @Value("${zookeeper.nodes.path}")
    private String root;

    @PostConstruct
    public void startZookeeper() throws Exception {
        logger.debug("start zookeeper ...");
        RetryPolicy retryPolicy = new RetryUntilElapsed(1000,1000);
        zk = CuratorFrameworkFactory.newClient(hosts, retryPolicy);
        zk.start();
    }

    @PreDestroy
    private void stopZookeeper() throws Exception {
        if(zk != null) {
            logger.debug("stop zookeeper ...");
            zk.close();
        }
    }

    @Override
    public boolean isMaster() {
        try {
            if(zk == null || zk.getZookeeperClient() == null || zk.getZookeeperClient().getZooKeeper() == null ){
                return false;
            }

            Long sessionId = zk.getZookeeperClient().getZooKeeper().getSessionId();
            List<String> children = null;

            if(zk.checkExists().forPath(root) != null){
                children = zk.getChildren().forPath(root);
            }
            if(children == null || children.isEmpty()){
                zk.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL).forPath(root+"/"+sessionId,"".getBytes());
                return true;
            }else{
                String child = children.get(0);
                if(sessionId.toString().equals(child)){
                    return true;
                }
            }
        } catch (Exception e) {
            logger.warn("create node failed....",e);
        }
        return false;
    }


    public String getHosts() {
        return hosts;
    }

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

    public Integer getRetry() {
        return retry;
    }

    public void setRetry(Integer retry) {
        this.retry = retry;
    }

    public Integer getConnectTimeout() {
        return connectTimeout;
    }

    public void setConnectTimeout(Integer connectTimeout) {
        this.connectTimeout = connectTimeout;
    }

    public Integer getSessionTimeout() {
        return sessionTimeout;
    }

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

    public String getRoot() {
        return root;
    }

    public void setRoot(String root) {
        this.root = root + "/master";
    }
}

配置文件:

zookeeper.registry.address=192.168.1.61:5181,192.168.1.61:5182,192.168.1.61:5181
zookeeper.retry.times = 3
zookeeper.connection.timeout = 30
zookeeper.session.timeout = 300
zookeeper.nodes.path = /tasks/nodes

    一旦有机器宕机,机器与zookeeper的链接会断开,临时节点就会失效,当下一台机器再次check的时候,发现临时节点不存在,就是创建临时节点,将自己作为定时任务的master,然后继续执行定时任务。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!