使用Redis中間件設計商品秒殺活動(使用Java多線程模擬高併發環境)

一、引入相關依賴

可以新建Spring或Maven工程,在pom文件中引入Jedis依賴:

    <dependencies>
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>2.9.0</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.58</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
    </dependencies>

二、核心代碼

SecKillDemo

package com.redis;

import java.util.List;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Transaction;

public class SecKillDemo implements Runnable {
    //這裏需要更改成自己的Redis服務器地址
    private Jedis jedis = new Jedis("10.128.*.*", 6379);
    //顧客
    private String customerName;
    //商品
    private String key;

    public SecKillDemo1(String customerName, String key) {
        this.customerName = customerName;
        this.key = key;
    }

    @Override
    public void run() {
        boolean success = false;
        String data;
        int currentNum;
        while (!success) {
        	//可重複搶購直到成功
            //通過watch實現redis的incr(原子遞增操作)
            jedis.watch(key);
            data = jedis.get(key);
            //獲取剩餘商品數
            currentNum = Integer.parseInt(data);
            if (currentNum > 0) {
                //開啓事務
                Transaction transaction = jedis.multi();
                //設置新值,如果key的值被其它連接的客戶端修改,那麼當前連接的exec命令將執行失敗
                //方式一 ,在事務中設置data的值爲原值減1,此時transaction.exec()的返回值的第一個元素是"OK"
                currentNum--;
                transaction.set(key, String.valueOf(currentNum));
             
                //方式二,在事務中對鍵data對應的值做減1操作,此時transaction.exec()的返回值的第一個元素是data對應的當前值
                //transaction.decrBy(key, Integer.valueOf(currentNum--));
                
                List res = transaction.exec();
                if (res.size() == 0) {
                	
                    String failUserInfo = "fail---" + customerName;
                    String failMsg = failUserInfo + ",搶購失敗,剩餘商品數量:"+ currentNum ;
                    // 將秒殺失敗的用戶信息存入Redis。
                    jedis.setnx(failUserInfo, failMsg);
                    System.out.println(customerName + " 搶購失敗");
                } else {
                    success = true;
					System.out.println(customerName + " 搶購成功,[" + key + "]剩餘:" + currentNum);
					String succUserInfo = customerName + " 搶購成功,[" + key + "]";
					String succMsg = succUserInfo + ",搶購成功,"	+ "剩餘商品數量:"+currentNum;
					System.out.println(res.get(0)+ "    " + succMsg);
					
					// 將秒殺成功的用戶信息存入Redis。
					jedis.setnx(succUserInfo, succMsg);
                	
                }
            } else {
                System.out.println("商品售空,活動結束!");
                System.exit(0);
            }
        }
    }
}

三、單元測試

SecKillDemoTest

package com.redis;

import java.util.Collections;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.junit.Before;
import org.junit.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
import redis.clients.jedis.Jedis;

public class SecKillDemoTest {
    //商品名稱
    private static String key = "華爲P40";
    //商品數量
    private static String num = "50";
    // 模擬用戶搶購最大併發數
    private static ExecutorService executorService = Executors.newFixedThreadPool(5);
    
    @Before
    public void before() {
        Jedis jedis = new Jedis("10.128.*.*");
        //命令包括set、get、del等
        //eval代表執行Lua語言的命令
        //KEYS[1]代表傳遞給Lua腳本的第一個key參數,
        //ARGV[1]代表第一個非key參數
        String script = "redis.call('del',KEYS[1]);return redis.call('set',KEYS[1],ARGV[1])";
        jedis.eval(script, Collections.singletonList(key), Collections.singletonList(num));
        jedis.close();
    }
    
    @Test
    public void test() {
    	
    }

    public static void main(String[] args) {
        try{
            for (int i = 1; i <= 100; i++) {
            	 executorService.submit(new SecKillDemo("顧客"+i,key));
            }
        }catch (Exception e) {
        	e.printStackTrace();
		}finally {
            executorService.shutdown();
        }
    }
}

執行步驟:
(1)執行junit單元測試方法,在redis中構建<key,value>數據。
(2)執行SecKillDemoTest 中的main方法。

執行結果如下:
在這裏插入圖片描述
從redis中查看結果如下:
在這裏插入圖片描述
在這裏插入圖片描述

參考文章:

使用Redis中間件解決商品秒殺活動中出現的超賣問題(使用Java多線程模擬高併發環境)
redis watch命令實現秒殺
Redis常用技術-----使用Lua語言

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章