1. 程式人生 > >redis 系列(十)java結合redis+lua 實現搶紅包經典案例

redis 系列(十)java結合redis+lua 實現搶紅包經典案例

使用lua指令碼來實現一個搶紅包的過程,lua具有原子特性,可以避免資料併發時多執行緒同時操作的問題

java程式碼結合lua實現搶紅包案例

單機版redis

package bhz.redis01;

import java.util.Random;
import java.util.concurrent.CountDownLatch;

import com.alibaba.fastjson.JSONObject;

import redis.clients.jedis.Jedis;

public class TestEval { 
	
    static String host = "192.168.1.115"; 
    static int port = 6379; 
    
    static int honBaoCount = 10000;  
      
    static int threadCount = 10;  
      
    static String hongBaoList = "hongBaoList"; 		//L1 為消費的紅包 預先生成的紅包 {id、money、userid(暫時未空)} 
    static String hongBaoConsumedList = "hongBaoConsumedList"; 		//L2 已消費的紅包佇列 
    static String hongBaoConsumedMap = "hongBaoConsumedMap";  		//去重的map
      
    static Random random = new Random();  
      
    /**
     * 		-- 函式:嘗試獲得紅包,如果成功,則返回json字串,如果不成功,則返回空  
	 *		-- 引數:紅包佇列名, 已消費的佇列名,去重的Map名,使用者ID  
	 *		-- 返回值:nil 或者 json字串,包含使用者ID:userId,紅包ID:id,紅包金額:money  
     *
	 *		-- 如果使用者已搶過紅包,則返回nil  
 	 *		if redis.call('hexists', KEYS[3], KEYS[4]) ~= 0 then  
	 * 		  return nil  
	 * 		else  
	 * 		  -- 先取出一個小紅包  
	 *		  local hongBao = redis.call('rpop', KEYS[1]);  
	 *		  if hongBao then  
	 *		    local x = cjson.decode(hongBao);  
	 *		    -- 加入使用者ID資訊  
	 *		    x['userId'] = KEYS[4];  
	 *		    local re = cjson.encode(x);  
	 *		    -- 把使用者ID放到去重的set裡  
	 *		    redis.call('hset', KEYS[3], KEYS[4], KEYS[4]);  
	 *		    -- 把紅包放到已消費佇列裡  
	 *		    redis.call('lpush', KEYS[2], re);  
	 *		    return re;  
	 *		  end  
	 *		end  
	 *		return nil 
     */
    static String tryGetHongBaoScript =   
//          "local bConsumed = redis.call('hexists', KEYS[3], KEYS[4]);\n"  
//          + "print('bConsumed:' ,bConsumed);\n"  
            "if redis.call('hexists', KEYS[3], KEYS[4]) ~= 0 then\n"  
            + "return nil\n"  
            + "else\n"  
            + "local hongBao = redis.call('rpop', KEYS[1]);\n"  
//          + "print('hongBao:', hongBao);\n"  
            + "if hongBao then\n"  
            + "local x = cjson.decode(hongBao);\n"  
            + "x['userId'] = KEYS[4];\n"  
            + "local re = cjson.encode(x);\n"  
            + "redis.call('hset', KEYS[3], KEYS[4], KEYS[4]);\n"  
            + "redis.call('lpush', KEYS[2], re);\n"  
            + "return re;\n"  
            + "end\n"  
            + "end\n"  
            + "return nil";  
      
    public static void main(String[] args) throws InterruptedException {  
        //generateTestData();  
        testTryGetHongBao();  
    }  
      
    static public void generateTestData() throws InterruptedException {  
        Jedis jedis = new Jedis(host, port); 
        jedis.flushAll();  
        final CountDownLatch latch = new CountDownLatch(threadCount);  
        for(int i = 0; i < threadCount; ++i) {  
            final int temp = i;  
            Thread thread = new Thread() {  
                public void run() {  
                    Jedis jedis = new Jedis(host);  
                    int per = honBaoCount/threadCount;  
                    JSONObject object = new JSONObject();  
                    for(int j = temp * per; j < (temp+1) * per; j++) {  
                        object.put("id", j);  
                        object.put("money", j);  
                        jedis.lpush(hongBaoList, object.toJSONString());  
                    }  
                    latch.countDown();  
                }  
            };  
            thread.start();  
        }  
        latch.await();  
        
        jedis.close();
    }  
      
    static public void testTryGetHongBao() throws InterruptedException {  
        final CountDownLatch latch = new CountDownLatch(threadCount);  
        
        long startTime = System.currentTimeMillis();
        System.err.println("start:" + startTime);  
        
        for(int i = 0; i < threadCount; ++i) {  
            final int temp = i;  
            Thread thread = new Thread() {  
                public void run() {  
                    Jedis jedis = new Jedis(host, port);  
                    String sha = jedis.scriptLoad(tryGetHongBaoScript);  
                    int j = honBaoCount/threadCount * temp;  
                    while(true) {  
                    	//搶紅包方法
                        Object object = jedis.eval(tryGetHongBaoScript, 4, 
                        		hongBaoList/*預生成的紅包佇列*/, 
                        		hongBaoConsumedList, /*已經消費的紅包佇列*/
                        		hongBaoConsumedMap, /*去重的map*/
                        		"" + j  /*使用者id*/
                        		);  
                        j++;  
                        if (object != null) {  
                        	//do something...
//                          System.out.println("get hongBao:" + object);  
                        }else {  
                            //已經取完了  
                            if(jedis.llen(hongBaoList) == 0)  
                                break;  
                        }  
                    }  
                    latch.countDown();  
                }  
            };  
            thread.start();  
        }  
          
        latch.await();  
        long costTime =  System.currentTimeMillis() - startTime;
        
        System.err.println("costTime:" + costTime);  
    }  
}  

看懂的轉發!