redis分佈式鎖,無須設置有效期,自動檢測hold鎖的節點是否存活

package six.com.crawler.common;java

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

import redis.clients.jedis.JedisCluster;redis

/**
 * @author 做者
 * @E-mail: 359852326@qq.com
 * @date 建立時間:2017年5月27日 下午3:13:14
 * 
 *       基於redis實現分佈式鎖,無需給鎖key加上過時時間,程序會自動檢測
 */
public class RedisLock {app

    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 + "_";
    }oop

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

    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();
    }.net

    /**
     * 節點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();
    }get

    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]);
    }

}  

相關文章
相關標籤/搜索