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

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

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Java教程 - springboot+redis分布式鎖實現模擬搶單

springboot+redis分布式鎖實現模擬搶單

2021-07-27 11:36神牛003 Java教程

這篇文章主要介紹了springboot+redis分布式鎖實現模擬搶單,小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧

本篇內容主要講解的是redis分布式鎖,這個在各大廠面試幾乎都是必備的,下面結合模擬搶單的場景來使用她;本篇不涉及到的redis環境搭建,快速搭建個人測試環境,這里建議使用docker;本篇內容節點如下:

  • jedis的nx生成鎖
  • 如何刪除鎖
  • 模擬搶單動作(10w個人開搶)

jedis的nx生成鎖

對于java中想操作redis,好的方式是使用jedis,首先pom中引入依賴:

?
1
2
3
4
<dependency>
 <groupid>redis.clients</groupid>
 <artifactid>jedis</artifactid>
</dependency>

對于分布式鎖的生成通常需要注意如下幾個方面:

  • 創建鎖的策略:redis的普通key一般都允許覆蓋,a用戶set某個key后,b在set相同的key時同樣能成功,如果是鎖場景,那就無法知道到底是哪個用戶set成功的;這里jedis的setnx方式為我們解決了這個問題,簡單原理是:當a用戶先set成功了,那b用戶set的時候就返回失敗,滿足了某個時間點只允許一個用戶拿到鎖。
  • 鎖過期時間:某個搶購場景時候,如果沒有過期的概念,當a用戶生成了鎖,但是后面的流程被阻塞了一直無法釋放鎖,那其他用戶此時獲取鎖就會一直失敗,無法完成搶購的活動;當然正常情況一般都不會阻塞,a用戶流程會正常釋放鎖;過期時間只是為了更有保障。

下面來上段setnx操作的代碼:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public boolean setnx(string key, string val) {
    jedis jedis = null;
    try {
      jedis = jedispool.getresource();
      if (jedis == null) {
        return false;
      }
      return jedis.set(key, val, "nx", "px", 1000 * 60).
          equalsignorecase("ok");
    } catch (exception ex) {
    } finally {
      if (jedis != null) {
        jedis.close();
      }
    }
    return false;
  }

這里注意點在于jedis的set方法,其參數的說明如:

  • nx:是否存在key,存在就不set成功
  • px:key過期時間單位設置為毫秒(ex:單位秒)

setnx如果失敗直接封裝返回false即可,下面我們通過一個get方式的api來調用下這個setnx方法:

?
1
2
3
4
@getmapping("/setnx/{key}/{val}")
 public boolean setnx(@pathvariable string key, @pathvariable string val) {
   return jediscom.setnx(key, val);
 }

訪問如下測試url,正常來說第一次返回了true,第二次返回了false,由于第二次請求的時候redis的key已存在,所以無法set成功

springboot+redis分布式鎖實現模擬搶單

由上圖能夠看到只有一次set成功,并key具有一個有效時間,此時已到達了分布式鎖的條件。

如何刪除鎖

上面是創建鎖,同樣的具有有效時間,但是我們不能完全依賴這個有效時間,場景如:有效時間設置1分鐘,本身用戶a獲取鎖后,沒遇到什么特殊情況正常生成了搶購訂單后,此時其他用戶應該能正常下單了才對,但是由于有個1分鐘后鎖才能自動釋放,那其他用戶在這1分鐘無法正常下單(因為鎖還是a用戶的),因此我們需要a用戶操作完后,主動去解鎖:

?
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
public int delnx(string key, string val) {
    jedis jedis = null;
    try {
      jedis = jedispool.getresource();
      if (jedis == null) {
        return 0;
      }
 
      //if redis.call('get','orderkey')=='1111' then return redis.call('del','orderkey') else return 0 end
      stringbuilder sbscript = new stringbuilder();
      sbscript.append("if redis.call('get','").append(key).append("')").append("=='").append(val).append("'").
          append(" then ").
          append("  return redis.call('del','").append(key).append("')").
          append(" else ").
          append("  return 0").
          append(" end");
 
      return integer.valueof(jedis.eval(sbscript.tostring()).tostring());
    } catch (exception ex) {
    } finally {
      if (jedis != null) {
        jedis.close();
      }
    }
    return 0;
  }

這里也使用了jedis方式,直接執行lua腳本:根據val判斷其是否存在,如果存在就del;

其實個人認為通過jedis的get方式獲取val后,然后再比較value是否是當前持有鎖的用戶,如果是那最后再刪除,效果其實相當;只不過直接通過eval執行腳本,這樣避免多一次操作了redis而已,縮短了原子操作的間隔。(如有不同見解請留言探討);同樣這里創建個get方式的api來測試:

?
1
2
3
4
@getmapping("/delnx/{key}/{val}")
 public int delnx(@pathvariable string key, @pathvariable string val) {
    return jediscom.delnx(key, val);
 }

注意的是delnx時,需要傳遞創建鎖時的value,因為通過et的value與delnx的value來判斷是否是持有鎖的操作請求,只有value一樣才允許del;

模擬搶單動作(10w個人開搶)

有了上面對分布式鎖的粗略基礎,我們模擬下10w人搶單的場景,其實就是一個并發操作請求而已,由于環境有限,只能如此測試;如下初始化10w個用戶,并初始化庫存,商品等信息,如下代碼:

?
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
//總庫存
  private long nkucuen = 0;
  //商品key名字
  private string shangpingkey = "computer_key";
  //獲取鎖的超時時間 秒
  private int timeout = 30 * 1000;
 
  @getmapping("/qiangdan")
  public list<string> qiangdan() {
 
    //搶到商品的用戶
    list<string> shopusers = new arraylist<>();
 
    //構造很多用戶
    list<string> users = new arraylist<>();
    intstream.range(0, 100000).parallel().foreach(b -> {
      users.add("神牛-" + b);
    });
 
    //初始化庫存
    nkucuen = 10;
 
    //模擬開搶
    users.parallelstream().foreach(b -> {
      string shopuser = qiang(b);
      if (!stringutils.isempty(shopuser)) {
        shopusers.add(shopuser);
      }
    });
 
    return shopusers;
  }

有了上面10w個不同用戶,我們設定商品只有10個庫存,然后通過并行流的方式來模擬搶購,如下搶購的實現:

?
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
/**
   * 模擬搶單動作
   *
   * @param b
   * @return
   */
  private string qiang(string b) {
    //用戶開搶時間
    long starttime = system.currenttimemillis();
 
    //未搶到的情況下,30秒內繼續獲取鎖
    while ((starttime + timeout) >= system.currenttimemillis()) {
      //商品是否剩余
      if (nkucuen <= 0) {
        break;
      }
      if (jediscom.setnx(shangpingkey, b)) {
        //用戶b拿到鎖
        logger.info("用戶{}拿到鎖...", b);
        try {
          //商品是否剩余
          if (nkucuen <= 0) {
            break;
          }
 
          //模擬生成訂單耗時操作,方便查看:神牛-50 多次獲取鎖記錄
          try {
            timeunit.seconds.sleep(1);
          } catch (interruptedexception e) {
            e.printstacktrace();
          }
 
          //搶購成功,商品遞減,記錄用戶
          nkucuen -= 1;
 
          //搶單成功跳出
          logger.info("用戶{}搶單成功跳出...所剩庫存:{}", b, nkucuen);
 
          return b + "搶單成功,所剩庫存:" + nkucuen;
        } finally {
          logger.info("用戶{}釋放鎖...", b);
          //釋放鎖
          jediscom.delnx(shangpingkey, b);
        }
      } else {
        //用戶b沒拿到鎖,在超時范圍內繼續請求鎖,不需要處理
//        if (b.equals("神牛-50") || b.equals("神牛-69")) {
//          logger.info("用戶{}等待獲取鎖...", b);
//        }
      }
    }
    return "";
  }

這里實現的邏輯是:

  • parallelstream():并行流模擬多用戶搶購
  • (starttime + timeout) >= system.currenttimemillis():判斷未搶成功的用戶,timeout秒內繼續獲取鎖
  • 獲取鎖前和后都判斷庫存是否還足夠
  • jediscom.setnx(shangpingkey, b):用戶獲取搶購鎖
  • 獲取鎖后并下單成功,最后釋放鎖:jediscom.delnx(shangpingkey, b)

再來看下記錄的日志結果:

springboot+redis分布式鎖實現模擬搶單

最終返回搶購成功的用戶:

springboot+redis分布式鎖實現模擬搶單

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

延伸 · 閱讀

精彩推薦
Weibo Article 1 Weibo Article 2 Weibo Article 3 Weibo Article 4 Weibo Article 5 Weibo Article 6 Weibo Article 7 Weibo Article 8 Weibo Article 9 Weibo Article 10 Weibo Article 11 Weibo Article 12 Weibo Article 13 Weibo Article 14 Weibo Article 15 Weibo Article 16 Weibo Article 17 Weibo Article 18 Weibo Article 19 Weibo Article 20 Weibo Article 21 Weibo Article 22 Weibo Article 23 Weibo Article 24 Weibo Article 25 Weibo Article 26 Weibo Article 27 Weibo Article 28 Weibo Article 29 Weibo Article 30 Weibo Article 31 Weibo Article 32 Weibo Article 33 Weibo Article 34 Weibo Article 35 Weibo Article 36 Weibo Article 37 Weibo Article 38 Weibo Article 39 Weibo Article 40
主站蜘蛛池模板: 蜜桃一区 | 91亚洲国产精品 | 狠狠av| 波多野结衣福利电影 | 国产91精品一区二区绿帽 | 欧美亚洲国产一区二区三区 | 国产一二三区在线观看 | 精品国产一区二区三区在线观看 | 啪啪网站免费 | 国产成人久久 | jizz中国zz女人18高潮 | 欧美一级淫片丝袜脚交 | 日韩欧美在线观看视频 | 亚洲免费一区 | 国产视频1区 | 九热精品| 91黄色片| 午夜操操操 | 狠狠操操 | 毛片免费在线播放 | 欧美在线亚洲 | 黄色小视频免费 | 欧美久久久久久久久久久 | 国产福利电影 | 中文字幕一二三区 | 日韩欧美自拍 | 亚洲人成在线播放 | 国产精品久久久999 一区二区三区视频免费在线观看 | 狠狠躁夜夜躁人人爽天天高潮 | 精品国产一区二区三区免费 | 午夜国产视频 | 精品久久国产字幕高潮 | 欧美日本精品 | 美国成人在线 | 久久久久成人精品免费播放动漫 | 亚洲午夜免费视频 | 欧美成人免费在线 | 在线99热| 久久久久久国产 | 国产亚洲人成网站在线观看 | 午夜影院免费 |