文档章节

redis分布式锁,无须设置有效期,自动检测hold锁的节点是否存活

sixliu
 sixliu
发布于 2017/05/27 16:25
字数 517
阅读 138
收藏 0

package six.com.crawler.common;

import java.lang.management.ManagementFactory;
import java.net.InetAddress;
import java.net.NetworkInterface;

import redis.clients.jedis.JedisCluster;

/**
 * @author 作者
 * @E-mail: 359852326@qq.com
 * @date 创建时间:2017年5月27日 下午3:13:14
 * 
 *       基于redis实现分布式锁,无需给锁key加上过期时间,程序会自动检测
 */
public class RedisLock {

    private static long SYSTEM_START_TIME = System.currentTimeMillis();
    private static String mac;
    private static String pid;
    private static String nodeKeepliveInfoPre;

    static {
        String name = ManagementFactory.getRuntimeMXBean().getName();
        pid = name.split("@")[0];
        try {
            InetAddress ia = InetAddress.getLocalHost();
            byte[] macBytes = NetworkInterface.getByInetAddress(ia).getHardwareAddress();
            StringBuffer sb = new StringBuffer("");
            for (int i = 0; i < macBytes.length; i++) {
                int temp = macBytes[i] & 0xff;
                String str = Integer.toHexString(temp);
                if (str.length() == 1) {
                    sb.append("0" + str);
                } else {
                    sb.append(str);
                }
            }
            mac = sb.toString().toUpperCase();
        } catch (Exception e) {
        }
        nodeKeepliveInfoPre = mac + "_" + pid + "_" + SYSTEM_START_TIME + "_";
    }

    private Thread keepliveThread;
    private JedisCluster jedisCluster;
    private String nodeKeepliveInfoKeyPre;
    private String nodeKeepliveInfoKey;
    private long loopKeepliveInterval;
    private int keepliveInfoExpire;
    private long checkLockIntervalTime;

    public RedisLock(JedisCluster jedisCluster, String nodeKeepLiveInfoKeyPre, long loopKeepliveInterval,
            long checkLockIntervalTime) {
        this.jedisCluster = jedisCluster;
        this.nodeKeepliveInfoKeyPre = nodeKeepLiveInfoKeyPre;
        this.nodeKeepliveInfoKey = getNodeKeepliveInfoKey(mac, pid, String.valueOf(SYSTEM_START_TIME));
        this.loopKeepliveInterval = loopKeepliveInterval;
        this.keepliveInfoExpire = (int) (loopKeepliveInterval) / 1000 * 2;
        this.checkLockIntervalTime = checkLockIntervalTime;
        initKeepLive();
    }

    /**
     * 节点mac+进程id+进程启动时间保证节点重启问题
     */
    private void initKeepLive() {
        keepliveThread = new Thread(() -> {
            String nodeInfo = null;
            while (true) {
                nodeInfo = nodeKeepliveInfoPre + String.valueOf(System.currentTimeMillis());
                jedisCluster.set(nodeKeepliveInfoKey, nodeInfo);
                jedisCluster.expire(nodeKeepliveInfoKey, keepliveInfoExpire);
                try {
                    Thread.sleep(loopKeepliveInterval);
                } catch (InterruptedException e) {
                }
            }

        }, "node-keeplive-thread");
        keepliveThread.setDaemon(true);
        keepliveThread.start();
    }

    public void lock(String lockKey) {
        while (true) {
            if (1 == jedisCluster.setnx(lockKey, getNodeLockInfo())) {
                break;
            }
            String nodeInfo = jedisCluster.get(lockKey);
            String nodeInfoKey = getNodeKeepliveInfoKey(nodeInfo);
            String lastKeepNodeInfo = jedisCluster.get(nodeInfoKey);
            do {
                try {
                    Thread.sleep(checkLockIntervalTime);// 这个时间需要根据节点刷新时间取一个合适值
                } catch (InterruptedException e) {
                }
                String tempNodeInfo = jedisCluster.get(nodeInfoKey);
                if (isNotKeeplive(lastKeepNodeInfo, tempNodeInfo)) {
                    // 证明节点挂了
                    unlock(lockKey);
                    break;
                } else {
                    lastKeepNodeInfo = tempNodeInfo;
                }
            } while (true);
        }
    }

    /**
     * 判断目标节点是否还在线
     * 
     * @param lastKeepliveInfo
     * @param newKeepliveInfo
     * @return
     */
    private boolean isNotKeeplive(String lastKeepliveInfo, String newKeepliveInfo) {
        String[] lastMeta = lastKeepliveInfo.split("_");
        String[] newMeta = newKeepliveInfo.split("_");
        // mac pid 启动时间 系统时间
        if (lastMeta[0] != newMeta[0]) {
            // 当前Hold key的节点已被其他节点占据
            return true;
        } else {
            if (lastMeta[1] != newMeta[1]) {
                // pid发生变化表示节点已经重启
                return true;
            } else {
                if (lastMeta[2] != newMeta[2]) {
                    // 启动时间发生变化表示节点已经重启
                    return true;
                } else {
                    if (lastMeta[3] != newMeta[3]) {
                        // 系统时间发生变化表示节点正常存活
                        return false;
                    } else {
                        return true;
                    }
                }
            }
        }
    }

    public void unlock(String lockKey) {
        jedisCluster.del(lockKey);
    }

    private String getNodeLockInfo() {
        return mac + "_" + pid + "_" + SYSTEM_START_TIME + "_" + System.currentTimeMillis();
    }

    private String getNodeKeepliveInfoKey(String mac, String pid, String systemStartTime) {
        String nodeKeepLiveInfoKey = nodeKeepliveInfoKeyPre + mac + pid + systemStartTime;
        return nodeKeepLiveInfoKey;
    }

    private String getNodeKeepliveInfoKey(String nodeLockInfo) {
        String[] meta = nodeLockInfo.split("_");
        return getNodeKeepliveInfoKey(meta[0], meta[1], meta[2]);
    }

}
 

© 著作权归作者所有

sixliu
粉丝 1
博文 3
码字总数 857
作品 0
深圳
程序员
私信 提问
加载中

评论(1)

sixliu
sixliu
加上这个以下可实现可重入:

private String getNodeLockInfo() {
return mac + "_" + pid + "_" + SYSTEM_START_TIME + "_" + Thread.currentThread().getId()
+ +System.currentTimeMillis();
}

private boolean isReentrant(String nodeLockInfo) {
String[] meta = nodeLockInfo.split("_");
if (mac.equals(meta0)
&&pid.equals(meta1)
&&SYSTEM_START_TIME == Long.valueOf(meta2)
&&Thread.currentThread().getId() == Long.valueOf(meta3)) {
return true;
}
return false;
}
RedLock算法-使用redis实现分布式锁服务

译自Redis官方文档 在多线程共享临界资源的场景下,分布式锁是一种非常重要的组件。 许多库使用不同的方式使用redis实现一个分布式锁管理。 其中有一部分简单的实现方式可靠性不足,可以通过...

豆花饭烧土豆
2018/08/18
0
0
史上最通俗分布式锁解读,看完不懂算我输

作者介绍 首先,分布式锁和我们平常讲到的锁原理基本一样,目的就是确保在多个线程并发时,只有一个线程在同一刻操作这个业务或者说方法、变量。 在一个进程中,也就是一个jvm或者说应用中,...

贾俊江
03/29
0
0
看一眼就能懂的“分布式锁”原理

作者:贾俊江 原文:https://mp.weixin.qq.com/s/gQfkOkR80MdtoTBP8hep6w 分布式锁和我们平常讲到的锁原理基本一样,目的就是确保在多个线程并发时,只有一个线程在同一刻操作这个业务或者说...

Java干货分享
06/10
0
0
最通俗分布式锁解读,,看不懂算我输

首先,分布式锁和我们平常讲到的锁原理基本一样,目的就是确保在多个线程并发时,只有一个线程在同一刻操作这个业务或者说方法、变量。 在一个进程中,也就是一个jvm或者说应用中,我们很容易...

Java干货分享
03/29
0
0
分布式锁原理的一些学习与思考redis分布式锁,zookeeper分布式锁

首先分布式锁和我们平常讲到的锁原理基本一样,目的就是确保,在多个线程并发时,只有一个线程在同一刻操作这个业务或者说方法、变量。 在一个进程中,也就是一个jvm 或者说应用中,我们很容...

java菜分享
03/09
0
0

没有更多内容

加载失败,请刷新页面

加载更多

好文:华杉:我等用功,不求日增,但求日减。减一分人欲,则增一分天理,这是何等简易!何等洒脱!

#写在前面1.怎么理解“减一分人欲,则增一分天理,这是何等简易!”?1)华杉提倡 “一劳永逸” 排除浪费,少干活,多赚钱,一战而定,降低作业成本。2)华杉提倡学海无涯,回头是岸...

阿锋zxf
26分钟前
3
0
vue 的bus总线

bus声明 global.bus = new Vue() 事件发送 controlTabbar () {global.bus.$emit('pickUp', 'ddd')}, 事件接收 global.bus.$on('pickUp', (res) => {this.isFocus = true})......

Js_Mei
31分钟前
2
0
大型系统演进之路-负载均衡演进

Nginx做负载均衡 通过Nginx的反向代理将请求分发到tomcat中,如果tomcat支持100并发,Nginx支持50000并发,理论上nginx把请求发送到500个tomcat就可以了。 LVS或F5做多个Nginx负载均衡 Tomc...

春哥大魔王的博客
36分钟前
4
0
Sqlite时间段查询中遇到的问题

问题: 我要查询DateTime时间其中的一段符合时间要求的数据,比如我要查询‘2019-06-06 16:50:00’至‘2019-06-06 16:59:00’这一段的数据 开始用这段代码 strSql= ("select * from Coll...

rainbowcode
39分钟前
2
0
大数据(hadoop-数据入库系统Sqoop原理架构)

Sqoop是什么 Sqoop:SQL-to-Hadoop 连接传统关系型数据库和Hadoop的桥梁 把关系型数据库的数据导入到 Hadoop 系统 ( 如 HDFS、HBase 和 Hive) 中; 把数据从 Hadoop 系统里抽取并导出到关系型...

这很耳东先生
54分钟前
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部