国产片侵犯亲女视频播放_亚洲精品二区_在线免费国产视频_欧美精品一区二区三区在线_少妇久久久_在线观看av不卡

服務器之家:專注于服務器技術及軟件下載分享
分類導航

Mysql|Sql Server|Oracle|Redis|MongoDB|PostgreSQL|Sqlite|DB2|mariadb|Access|數(shù)據(jù)庫技術|

服務器之家 - 數(shù)據(jù)庫 - Redis - Redis實現(xiàn)分布式鎖和等待序列的方法示例

Redis實現(xiàn)分布式鎖和等待序列的方法示例

2019-11-25 15:14小小小LIN子 Redis

這篇文章主要介紹了Redis實現(xiàn)分布式鎖和等待序列的方法示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧

在集群下,經(jīng)常會因為同時處理發(fā)生資源爭搶和并發(fā)問題,但是我們都知道同步鎖 synchronized 、 cas 、 ReentrankLock 這些鎖的作用范圍都是 JVM ,說白了在集群下沒啥用。這時我們就需要能在多臺 JVM 之間決定執(zhí)行順序的鎖了,現(xiàn)在分布式鎖主要有 redis 、 Zookeeper 實現(xiàn)的,還有數(shù)據(jù)庫的方式,不過性能太差,也就是需要一個第三方的監(jiān)管。

背景

最近在做一個消費 Kafka 消息的時候發(fā)現(xiàn),由于線上的消費者過多,經(jīng)常會遇到,多個機器同時處理一個主鍵類型的數(shù)據(jù)的情況發(fā)生,如果最后是執(zhí)行更新操作的話,也就是一個更新順序的問題,但是如果恰好都需要插入數(shù)據(jù)的時候,會出現(xiàn)主鍵重復的問題。這是生產(chǎn)上不被允許的(因為公司有異常監(jiān)管的機制,扣分啥的),這是就需要個分布式鎖了,斟酌后用了 Redis 的實現(xiàn)方式(因為網(wǎng)上例子多)

分析

redis 實現(xiàn)的分布式鎖,實現(xiàn)原理是 set 方法,因為多個線程同時請求的時候,只有一個線程可以成功并返回結(jié)果,還可以設置有效期,來避免死鎖的發(fā)生,一切都是這么的完美,不過有個問題,在 set 的時候,會直接返回結(jié)果,成功或者失敗,不具有阻塞效果,需要我們自己對失敗的線程進程處理,有兩種方式

  • 丟棄
  • 等待重試 由于我們的系統(tǒng)需要這些數(shù)據(jù),那么只能重新嘗試獲取。這里使用 redis 的 List 類型實現(xiàn)等待序列的作用

代碼

直接上代碼 其實直接redis的工具類就可以解決了

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
package com.test
import redis.clients.jedis.Jedis;
 
import java.util.Collections;
import java.util.List;
 
/**
 * @desc redis隊列實現(xiàn)方式
 * @anthor
 * @date
 **/
public class RedisUcUitl {
 
  private static final String LOCK_SUCCESS = "OK";
  private static final String SET_IF_NOT_EXIST = "NX";
  private static final String SET_WITH_EXPIRE_TIME = "PX";
 
  private static final Long RELEASE_SUCCESS = 1L;
 
  private RedisUcUitl() {
 
  }
  /**
   * logger
   **/
 
  /**
   * 存儲redis隊列順序存儲 在隊列首部存入
   *
   * @param key  字節(jié)類型
   * @param value 字節(jié)類型
   */
  public static Long lpush(Jedis jedis, final byte[] key, final byte[] value) {
 
    return jedis.lpush(key, value);
  
  }
 
  /**
   * 移除列表中最后一個元素 并將改元素添加入另一個列表中 ,當列表為空時 將阻塞連接 直到等待超時
   *
   * @param srckey
   * @param dstkey
   * @param timeout 0 表示永不超時
   * @return
   */
  public static byte[] brpoplpush(Jedis jedis,final byte[] srckey, final byte[] dstkey, final int timeout) {
 
    return jedis.brpoplpush(srckey, dstkey, timeout);
 
  }
 
  /**
   * 返回制定的key,起始位置的redis數(shù)據(jù)
   * @param redisKey
   * @param start
   * @param end -1 表示到最后
   * @return
   */
  public static List<byte[]> lrange(Jedis jedis,final byte[] redisKey, final long start, final long end) {
    
    return jedis.lrange(redisKey, start, end);
  }
 
  /**
   * 刪除key
   * @param redisKey
   */
  public static void delete(Jedis jedis, final byte[] redisKey) {
    
     return jedis.del(redisKey);
  }
 
  /**
   * 嘗試加鎖
   * @param lockKey key名稱
   * @param requestId 身份標識
   * @param expireTime 過期時間
   * @return
   */
  public static boolean tryGetDistributedLock(Jedis jedis,final String lockKey, final String requestId, final int expireTime) {
    String result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);
    return LOCK_SUCCESS.equals(result);
 
  }
 
  /**
   * 釋放鎖
   * @param lockKey key名稱
   * @param requestId 身份標識
   * @return
   */
  public static boolean releaseDistributedLock(Jedis jedis,final String lockKey, final String requestId) {
    final String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
    jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));
 
    return RELEASE_SUCCESS.equals(result);
 
  }
}

業(yè)務邏輯主要代碼如下

1.先消耗隊列中的

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
while(true){
  // 消費隊列
  try{
    // 被放入redis隊列的數(shù)據(jù) 序列化后的
    byte[] bytes = RedisUcUitl.brpoplpush(keyStr.getBytes(UTF_8), dstKeyStr.getBytes(UTF_8), 1);
    if(bytes == null || bytes.isEmpty()){
      // 隊列中沒數(shù)據(jù)時退出
      break;
    }
    // 反序列化對象
    Map<String, Object> singleMap = (Map<String, Object>) ObjectSerialUtil.bytesToObject(bytes);
    // 塞入唯一的值 防止被其他線程誤解鎖
    String requestId = UUID.randomUUID().toString();
    boolean lockGetFlag = RedisUcUitl.tryGetDistributedLock(keyStr,requestId, 100);
    if(lockGetFlag){
      // 成功獲取鎖 進行業(yè)務處理
      //TODO
      // 處理完畢釋放鎖
      boolean freeLock = RedisUcUitl.releaseDistributedLock(keyStr, requestId);
 
    }else{
      // 未能獲得鎖放入等待隊列
     RedisUcUitl.lpush(keyStr.getBytes(UTF_8), ObjectSerialUtil.objectToBytes(param));
  
    }
    
  }catch(Exception e){
    break;
  }
  
}

2.處理最新接到的數(shù)據(jù)

同樣是走嘗試獲取鎖,獲取不到放入隊列的流程

一般序列化用 fastJson 之列的就可以了,這里用的是 JDK 自帶的,工具類如下

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
public class ObjectSerialUtil {
 
  private ObjectSerialUtil() {
//    工具類
  }
 
  /**
   * 將Object對象序列化為byte[]
   *
   * @param obj 對象
   * @return byte數(shù)組
   * @throws Exception
   */
  public static byte[] objectToBytes(Object obj) throws IOException {
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    ObjectOutputStream oos = new ObjectOutputStream(bos);
    oos.writeObject(obj);
    byte[] bytes = bos.toByteArray();
    bos.close();
    oos.close();
    return bytes;
  }
 
 
  /**
   * 將bytes數(shù)組還原為對象
   *
   * @param bytes
   * @return
   * @throws Exception
   */
  public static Object bytesToObject(byte[] bytes) {
    try {
      ByteArrayInputStream bin = new ByteArrayInputStream(bytes);
      ObjectInputStream ois = new ObjectInputStream(bin);
      return ois.readObject();
    } catch (Exception e) {
      throw new BaseException("反序列化出錯!", e);
    }
  }
}

以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。

原文鏈接:https://juejin.im/post/5d077d04f265da1b5f265661

延伸 · 閱讀

精彩推薦
  • Redisredis 交集、并集、差集的具體使用

    redis 交集、并集、差集的具體使用

    這篇文章主要介紹了redis 交集、并集、差集的具體使用,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友...

    xiaojin21cen10152021-07-27
  • RedisRedis全量復制與部分復制示例詳解

    Redis全量復制與部分復制示例詳解

    這篇文章主要給大家介紹了關于Redis全量復制與部分復制的相關資料,文中通過示例代碼介紹的非常詳細,對大家學習或者使用Redis爬蟲具有一定的參考學習...

    豆子先生5052019-11-27
  • Redisredis中如何使用lua腳本讓你的靈活性提高5個逼格詳解

    redis中如何使用lua腳本讓你的靈活性提高5個逼格詳解

    這篇文章主要給大家介紹了關于redis中如何使用lua腳本讓你的靈活性提高5個逼格的相關資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具...

    一線碼農(nóng)5812019-11-18
  • RedisRedis如何實現(xiàn)數(shù)據(jù)庫讀寫分離詳解

    Redis如何實現(xiàn)數(shù)據(jù)庫讀寫分離詳解

    Redis的主從架構(gòu),能幫助我們實現(xiàn)讀多,寫少的情況,下面這篇文章主要給大家介紹了關于Redis如何實現(xiàn)數(shù)據(jù)庫讀寫分離的相關資料,文中通過示例代碼介紹...

    羅兵漂流記6092019-11-11
  • Redisredis實現(xiàn)排行榜功能

    redis實現(xiàn)排行榜功能

    排行榜在很多地方都能使用到,redis的zset可以很方便地用來實現(xiàn)排行榜功能,本文就來簡單的介紹一下如何使用,具有一定的參考價值,感興趣的小伙伴們...

    乘月歸5022021-08-05
  • Redis詳解Redis復制原理

    詳解Redis復制原理

    與大多數(shù)db一樣,Redis也提供了復制機制,以滿足故障恢復和負載均衡等需求。復制也是Redis高可用的基礎,哨兵和集群都是建立在復制基礎上實現(xiàn)高可用的...

    李留廣10222021-08-09
  • RedisRedis的配置、啟動、操作和關閉方法

    Redis的配置、啟動、操作和關閉方法

    今天小編就為大家分享一篇Redis的配置、啟動、操作和關閉方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧 ...

    大道化簡5312019-11-14
  • RedisRedis 事務知識點相關總結(jié)

    Redis 事務知識點相關總結(jié)

    這篇文章主要介紹了Redis 事務相關總結(jié),幫助大家更好的理解和學習使用Redis,感興趣的朋友可以了解下...

    AsiaYe8232021-07-28
主站蜘蛛池模板: 中文字幕第二页 | 欧美日韩在线一区 | 欧美成人黄色网 | 久久精品久久久久久 | 亚洲欧美一区二区三区国产精品 | 亚洲性生活免费视频 | 久久精品视频免费 | 成人av在线网站 | 一区二区三区无码高清视频 | 成人精品一区二区 | 国产午夜精品一区二区三区视频 | 精品久久久久久久久久久久 | 99热首页 | 久久精品一区 | 网站毛片 | 黄色成人在线 | 欧美黑人一级爽快片淫片高清 | 亚洲欧美一区二区三区在线 | 精品电影 | 成人激情视频 | 欧美日韩中文字幕在线 | 91亚洲国产成人久久精品网站 | 91看片淫黄大片一级在线观看 | 三级黄色视频毛片 | 成人午夜毛片 | 99久久国产免费 | 国产精品久久久久久久久久久久久 | 久久久久久天堂 | 色av网| 亚洲精品久久久久久下一站 | 成人免费看片 | 性做久久久久久 | 亚洲国产欧美日韩 | 欧美精品1区2区3区 日本电影中文字幕 | 日韩欧美在线观看视频 | 欧美日韩视频在线 | 成人av片在线观看 | 亚洲成人av| 91久久精品一区二区二区 | 中国特级毛片 | 久久精品六 |