redisロックは分布式番号取りの構想を実現する

21786 ワード

最近分布式ロックというものを見て、他の大神の説に基づいて、いくつかの基本的な実現方法をまとめました.
1、データベース楽観ロック
2、redis錠
3、zookeeper
暇なときは、比較的簡単に実現できるredisロックを書きました.説明が苦手なので、直接コードを貼りましょう.
1、mavenは関連jarパッケージを持ち込む

   redis.clients
   jedis
   2.9.0

2、ロックの取得とロックの解除に関するコード
public class RedisLock {

    private static JedisPool jedisPool;

    static {
        JedisPoolConfig config = new JedisPoolConfig();
        config.setMaxTotal(100);
        config.setMaxIdle(100);
        config.setMaxWaitMillis(3000);
        config.setTestOnBorrow(false);
        config.setMinIdle(10);
        jedisPool = new JedisPool(config, "127.0.0.1", 6379);
    }

    private static Jedis getClient() {
        return jedisPool.getResource();
    }

    private static String key = "redis.lock";
    private static String val = "true";
    private static int expire = 60; //    

    //   
    public static boolean getLock() {
        for (; ; ) {
            Jedis jedis = null;
            try {
                jedis = getClient();
                Long result = jedis.setnx(key, val);
                if (result == 1) {
                    jedis.expire(key, expire);
                    return true;
                }
                System.out.println(Thread.currentThread().getName() + "    ");
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            } finally {
                jedisPool.returnResource(jedis);
            }
        }
    }

    //   
    public static void relaseLock() {
        getClient().expire(key, -1);
        System.out.println("     ");
    }
}

3、簡単なデータ記録センターで、一括で順序番号を取得する方法を提供する
public class DataRange {

    private Long start;

    private Long end;

    public Long getStart() {
        return start;
    }

    public void setStart(Long start) {
        this.start = start;
    }

    public void setEnd(Long end) {
        this.end = end;
    }

    public Long getEnd() {
        return end;
    }

    @Override
    public String toString() {
        return "DataRange{" +
                "start=" + start +
                ", end=" + end +
                '}';
    }
}
public class DataCenter {

    private static Long num = 0L;

    private static Integer step = 500;

    public static DataRange getRangeNos() throws Exception {
        if (RedisLock.getLock()) {
            DataRange range = new DataRange();
            range.setStart(num);
            num += step;
            range.setEnd(num - 1);

//            TimeUnit.SECONDS.sleep(10);
            RedisLock.relaseLock();
            return range;
        }
        throw new Exception("       ");
    }
}

4、1つの補充流水番号と日付が完全な流水番号を生成する補助類
public class DataUtil {

    private static final int length = 8;

    private static final String addc = "0";

    private static SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd");

    public static String complete(Long l) throws Exception {
        String dateStr = format.format(new Date());
        String ls = l.toString();
        int lsl = ls.length();
        if (lsl < length) {
            StringBuilder sb = new StringBuilder(dateStr);
            for (int i = 0; i < length - lsl; i++) {
                sb.append(addc);
            }
            return sb.append(ls).toString();
        } else if (lsl == length) {
            return dateStr + ls;
        } else {
            throw new Exception("out of range");
        }
    }
}

5、呼び出しクライアントを作成し、ここではスレッドで表す
public class MyThread extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            try {
                DataRange range = DataCenter.getRangeNos();
                System.out.println(this.getName() + "       " + range);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

6、Testクラスの作成
public class Test {

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            Thread thread = new MyThread();
            thread.start();
        }
    }
}

出力結果:
Thread-6スピンThread-2スピンThread-5スピンThread-0スピンThread-4スピンThread-8スピンThread-7スピンレリーズロック成功Thread-3配列番号DataRange{start=0,end=499}レリーズロック成功Thread-3配列番号DataRange{start=500,end=999}レリーズロック成功Thread-3配列番号DataRange{start=1000,end=1499}リリースロック成功Thread-3取得配列番号DataRange{start=1500,end=1999}リリースロック成功Thread-3取得配列番号DataRange{start=2000,end=2499}Thread-3スピンリリースロック成功Thread-1取得配列番号DataRange{start=2500,end=2999}リリースロック成功Thread-1取得配列番号DataRange{start=3000,end=3499}リリースロック成功Thread-1取得配列番号DataRange{start=3500,end=3999}Thread-1スピンレリーズロック成功Thread-9配列番号取得DataRange{start=4000,end=4499}レリーズロック成功Thread-9配列番号取得DataRange{start=4500,end=4999}レリーズロック成功Thread-9配列番号取得DataRange{start=5000,end=5499}レリーズロック成功Thread-9配列番号取得Datange{start=5500,end=5999}解放ロック成功Thread-9配列番号DataRange{start=6000,end=6499}解放ロック成功Thread-9配列番号DataRange{start=6500,end=6999}解放ロック成功Thread-9配列番号DataRange{start=7000,end=7499}解放ロック成功Thread-9配列番号DataRange{start=7500,end=7999}解放ロック成功Thread-9配列番号DataRange{start=8000,end=8499}リリースロック成功Thread-9取得配列番号DataRange{start=8500,end=8999}Thread-2スピン中Thread-5スピンリリースロック成功Thread-6取得配列番号DataRange{start=9000,end=9499}リリースロック成功Thread-6取得配列番号DataRange{start=9500,end=999}リリースロック成功Thread-6取得配列番号DataRange{start=10000,end=10499}解放ロック成功Thread-6配列番号DataRange{start=10500,end=10999}Thread-4正スピンThread-8正スピンThread-6正スピン解放ロック成功Thread-0配列番号DataRange{start=1000,end=1499}解放ロック成功Thread-0配列番号DataRange{start=1500,end=1999}Thread-0正スピン解放ロック成功Thread-7配列番号DataRange{start=12000,end=12499}リリースロック成功Thread-7取得配列番号DataRange{start=12500,end=12999}リリースロック成功Thread-7取得配列番号DataRange{start=13000,end=13499}リリースロック成功Thread-7取得配列番号DataRange{start=13500,end=13999}リリースロック成功Thread-7取得配列番号DataRange{start=1400,end=14499}Thread-7スピンリリースロック成功Thread-3取得配列番号DataRange{start=14500,end=14999}リリースロック成功Thread-3取得配列番号DataRange{start=15000,end=15499}リリースロック成功Thread-3取得配列番号DataRange{start=15500,end=15999}リリースロック成功Thread-3取得配列番号DataRange{start=16099}リリースロック成功Thread-3取得配列番号DataRange{start=16500,end=16999}リリースロック成功Thread-3取得配列番号DataRange{start=16500,end=16999}リリースロック成功Thread-1取得配列番号DataRange{start=1700,end=17499}レリーズロック成功Thread-1配列番号DataRange{start=17500,end=17999}レリーズロック成功Thread-1配列番号DataRange{start=18000,end=18499}レリーズロック成功Thread-1配列番号DataRange{start=18500,end=18999}レリーズロック成功Thread-1配列番号DataRange{start=1900,end=19499}レリーズロック成功Thread-1配列番号DataRange{start=19500,end=19999}リリースロック成功Thread-1取得配列番号DataRange{start=20000,end=20499}リリースロック成功Thread-2取得配列番号DataRange{start=20500,end=20999}Thread-2スピンリリースロック成功Thread-5取得配列番号DataRange{start=2100,end=21499}リリースロック成功Thread-5取得配列番号Datange{start=21500,end=21999}リリースロック成功Thread-5取得配列番号DataRange{start=2000,end=22499}リリースロック成功Thread-5取得配列番号DataRange{start=2500,end=22999}リリースロック成功Thread-5取得配列番号DataRange{start=23000,end=23499}リリースロック成功Thread-5取得配列番号DataRange{start=23500,end=23999}Thread-8スピンThread-4スピンレリーズロック成功Thread-6配列番号DataRange{start=24500,end=24999}レリーズロック成功Thread-5配列番号DataRange{start=24000,end=244999}Thread-5スピンレリーズロック成功Thread-6配列番号DataRange{start=25000,end=254999}レリーズロック成功Thread-6配列番号DataRange{start=25500,end=25999}リリースロック成功Thread-6取得配列番号DataRange{start=25600,end=26499}リリースロック成功Thread-6取得配列番号DataRange{start=26500,end=26999}リリースロック成功Thread-6取得配列番号DataRange{start=2700,end=27499}リリースロック成功Thread-0取得配列番号DataRange{start=2700,end=27999}リリースロック成功Thread-0取得配列番号DataRange{start=28000,end=28499}解放ロック成功Thread-0配列番号DataRange{start=28500,end=28999}解放ロック成功Thread-0配列番号DataRange{start=29000,end=29999}解放ロック成功Thread-0配列番号DataRange{start=29500,end=29999}解放ロック成功Thread-0配列番号DataRange{start=30000,end=30499}解放ロック成功Thread-0配列番号DataRange{start=30999}解放ロック成功Thread-0配列番号Datange{start=30500,end=30999}解放ロック成功Thread-0配列番号DataRange{start=31000,end=31499}解放ロック成功Thread-7配列番号DataRange{start=31500,end=31999}解放ロック成功Thread-7配列番号DataRange{start=32000,end=32499}解放ロック成功Thread-7配列番号DataRange{start=32500,end=32999}解放ロック成功Thread-7配列番号DataRange{start=33000,end=33499}解放ロック成功Thread-7配列番号DataRange{start=33500,end=33999}解放ロック成功Thread-2配列番号DataRange{start=34000,end=34499}解放ロック成功Thread-2配列番号DataRange{start=34500,end=34999}解放ロック成功Thread-2配列番号DataRange{start=35000,end=35499}解放ロック成功Thread-2配列番号DataRange{start=35500,end=35999}Thread-8スピンレリーズロック成功Thread-2配列番号DataRange{start=36000,end=36499}Thread-5スピンThread-2スピンレリーズロック成功Thread-4配列番号DataRange{start=36500,end=36999}レリーズロック成功Thread-4配列番号DataRange{start=37000,end=37499}レリーズロック成功Thread-4配列番号DataRange{start=37500,end=37999}解放ロック成功Thread-4配列番号DataRange{start=38000,end=38499}解放ロック成功Thread-4配列番号DataRange{start=38500,end=38999}解放ロック成功Thread-4配列番号DataRange{start=39000,end=39499}解放ロック成功Thread-4配列番号DataRange{start=39500,end=39999}解放ロック成功Thread-4配列番号DataRange{start=40000,end=40499}解放ロック成功Thread-4配列番号DataRange{start=40500,end=40999}解放ロック成功Thread-4配列番号DataRange{start=41000,end=41499}解放ロック成功Thread-8配列番号DataRange{start=41500,end=41999}解放ロック成功Thread-8配列番号DataRange{start=42000,end=42499}解放ロック成功Thread-8配列番号DataRange{start=42999}リリースロック成功Thread-8配列番号DataRange{start=43000,end=43499}Thread-8スピンリリースロック成功Thread-5配列番号DataRange{start=43500,end=43999}Thread-2スピンリリースロック成功Thread-5配列番号DataRange{start=44000,end=44499}リリースロック成功Thread-5配列番号DataRange{start=44500,end=44999}Thread-2スピンレリーズロック成功Thread-8配列番号DataRange{start=45000,end=45499}レリーズロック成功Thread-8配列番号DataRange{start=45500,end=45999}レリーズロック成功Thread-8配列番号DataRange{start=46000,end=46499}レリーズロック成功Thread-8配列番号DataRange{start=4600,end=46999}レリーズロック成功Thread-8配列番号DataRange{start=46500,end=46999}レリーズロック成功Thread-8配列番号DataRange{start=47000,end=47499}解放ロック成功Thread-8配列番号DataRange{start=47500,end=47999}解放ロック成功Thread-2配列番号DataRange{start=48000,end=48499}解放ロック成功Thread-2配列番号DataRange{start=48500,end=48999}解放ロック成功Thread-2配列番号DataRange{start=49000,end=499}解放ロック成功Thread-2配列番号DataRange{start=49500, end=49999}
まとめ:redisのsetnxを使用して、どのクライアントがロックを取得するかを制御します(現在、上記のロック取得方法では同期の問題があり、keyvalueの設定と期限切れの設定が同期しないと、場合によってはデッドロックになります).取得したフロー番号は、毎回複数取得され、一定期間使用できます.本機のテストのため、学習の参考に供するだけで、必要なのは自分でコードを修正して完備することができます.
ここでdatacenterは、データベースまたはredisキャッシュストレージに変更できます.
あるいは、データベース楽観ロックを直接使用して実装してもよい.