基於redis的高並發秒殺的JAVA-DEMO實現!


在Redis的事務中,WATCH命令可用於提供CAS(check-and-set)功能。假設我們通過WATCH命令在事務執行之前監控了多個Keys,倘若在WATCH之后有任何Key的值發生了變化,EXEC命令執行的事務都將被放棄,同時返回Null multi-bulk應答以通知調用者事務執行失敗。例如,我們再次假設Redis中並未提供incr命令來完成鍵值的原子性遞增,如果要實現該功能,我們只能自行編寫相應的代碼。其偽碼如下:
      val = GET mykey
      val = val + 1
      SET mykey $val
      以上代碼只有在單連接的情況下才可以保證執行結果是正確的,因為如果在同一時刻有多個客戶端在同時執行該段代碼,那么就會出現多線程程序中經常出現的一種錯誤場景--競態爭用(race condition)。比如,客戶端A和B都在同一時刻讀取了mykey的原有值,假設該值為10,此后兩個客戶端又均將該值加一后set回Redis服務器,這樣就會導致mykey的結果為11,而不是我們認為的12。為了解決類似的問題,我們需要借助WATCH命令的幫助,見如下代碼:
      WATCH mykey
      val = GET mykey
      val = val + 1
      MULTI
      SET mykey $val
      EXEC
      和此前代碼不同的是,新代碼在獲取mykey的值之前先通過WATCH命令監控了該鍵,此后又將set命令包圍在事務中,這樣就可以有效的保證每個連接在執行EXEC之前,如果當前連接獲取的mykey的值被其它連接的客戶端修改,那么當前連接的EXEC命令將執行失敗。這樣調用者在判斷返回值后就可以獲悉val是否被重新設置成功。

      根據這樣的思路,我們在JAVA下進行實現:

  新建一個項目,首先引入JAVA的redis操作庫:Jedis,這里用的是jedis-2.9.0.jar

     新建一個類:MyRedistest.class做線程操作

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
package  com.myredistest;
import  java.util.Random;
import  java.util.concurrent.ExecutorService;
import  java.util.concurrent.Executors;
 
import  redis.clients.jedis.Jedis;
 
/**
  * redis
  *
  * @author 10255_000
  *
  */
 
public  class  MyRedistest {
      public  static  void  main(String[] args) {
             final  String watchkeys =  "watchkeys" ;
             ExecutorService executor = Executors.newFixedThreadPool( 20 );   //20個線程池並發數
 
             final  Jedis jedis =  new  Jedis( "192.168.56.101" 6379 );
             jedis.set(watchkeys,  "100" ); //設置起始的搶購數
            // jedis.del("setsucc", "setfail");
             jedis.close();
             
             for  ( int  i =  0 ; i <  1000 ; i++) { //設置1000個人來發起搶購
                 executor.execute( new  MyRunnable( "user" +getRandomString( 6 )));
             }
             executor.shutdown();
         }
 
      
      public  static  String getRandomString( int  length) {  //length是隨機字符串長度
             String base =  "abcdefghijklmnopqrstuvwxyz0123456789" ;  
             Random random =  new  Random();  
             StringBuffer sb =  new  StringBuffer();  
             for  ( int  i =  0 ; i < length; i++) {  
                 int  number = random.nextInt(base.length());  
                 sb.append(base.charAt(number));  
             }  
             return  sb.toString();  
         
}

   建一個類:MyRunnable.class 實現Runnable做線程操作:

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
package  com.myredistest;
 
import  java.util.List;
 
import  redis.clients.jedis.Jedis;
import  redis.clients.jedis.Transaction;
 
public  class  MyRunnable  implements  Runnable {
 
     String watchkeys =  "watchkeys" ; // 監視keys
     Jedis jedis =  new  Jedis( "192.168.56.101" 6379 );
     String userinfo;
     public  MyRunnable() {
     }
     public  MyRunnable(String uinfo) {
         this .userinfo=uinfo;
     }
     @Override
     public  void  run() {
         try  {
             jedis.watch(watchkeys); // watchkeys
 
             String val = jedis.get(watchkeys);
             int  valint = Integer.valueOf(val);
            
             if  (valint <=  100  && valint>= 1 ) {
            
                  Transaction tx = jedis.multi(); // 開啟事務
                // tx.incr("watchkeys");
                 tx.incrBy( "watchkeys" , - 1 );
 
                 List<Object> list = tx.exec(); // 提交事務,如果此時watchkeys被改動了,則返回null
                 
                 if  (list ==  null  ||list.size()== 0 ) {
 
                     String failuserifo =  "fail" +userinfo;
                     String failinfo= "用戶:"  + failuserifo +  "商品爭搶失敗,搶購失敗" ;
                     System.out.println(failinfo);
                     /* 搶購失敗業務邏輯 */
                     jedis.setnx(failuserifo, failinfo);
                 else  {
                     for (Object succ : list){
                          String succuserifo = "succ" +succ.toString() +userinfo ;
                          String succinfo= "用戶:"  + succuserifo +  "搶購成功,當前搶購成功人數:"
                                  + ( 1 -(valint- 100 ));
                          System.out.println(succinfo);
                          /* 搶購成功業務邏輯 */
                          jedis.setnx(succuserifo, succinfo);
                     }
                     
                 }
 
             else  {
                 String failuserifo = "kcfail"  +  userinfo;
                 String failinfo1= "用戶:"  + failuserifo +  "商品被搶購完畢,搶購失敗" ;
                 System.out.println(failinfo1);
                 jedis.setnx(failuserifo, failinfo1);
                 // Thread.sleep(500);
                 return ;
             }
 
         catch  (Exception e) {
             e.printStackTrace();
         finally  {
             jedis.close();
         }
 
     }
     
   
}

  執行MyRedistest ,查看redis中插入的key值

 

 

 

 

 

轉載:https://www.cnblogs.com/longtaosoft/p/6627568.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM