使用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语言

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