本文主要通过一个简单的例子模拟实现秒杀情景,其中主要使用Redis事物进行实现spring boot为提供方便的环境。
pom.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.6.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.leftso.demo</groupId>
<artifactId>demo-redis-seckill</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo-redis-seckill</name>
<description>Redis 实现产品秒杀</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.0.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
配置redis的连接池,这个根据自己需求改。这里测试用。
package com.leftso.demo.demoredisseckill;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
@Configuration
public class JedisConfig {
@Bean
public JedisPool jedisPool(){
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
// 设置配置
jedisPoolConfig.setMaxTotal(1024);
jedisPoolConfig.setMaxIdle(100);
jedisPoolConfig.setMaxWaitMillis(100);
jedisPoolConfig.setTestOnBorrow(false);//jedis 第一次启动时,会报错
jedisPoolConfig.setTestOnReturn(true);
JedisPool pool=new JedisPool(jedisPoolConfig,"127.0.0.1",6379);
return pool;
}
}
package com.leftso.demo.demoredisseckill.service;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.Transaction;
import java.util.List;
public class Seckill implements Runnable {
private JedisPool jedisPool;
private String userName;
private String productKey;
public Seckill(JedisPool jedisPool, String userName, String productKey) {
this.jedisPool = jedisPool;
this.userName = userName;
this.productKey = productKey;
}
@Override
public void run() {
Jedis jedis=jedisPool.getResource();
try {
jedis.watch(productKey);
String val=jedis.get(productKey);
int valInt=Integer.valueOf(val);
if (valInt>=1){
Transaction tx=jedis.multi();
tx.incrBy(productKey,-1);//原子操作
List<Object> list=tx.exec();
if (list==null||list.isEmpty()){
System.out.println("用户:"+userName+" 抢购失败。");
this.run();//再抢
}else{
System.out.println("用户:"+userName+ " 抢购成功!!!");
// jedis.setnx(productKey,)
jedis.rpush(productKey+"user",userName);//成功用户添加入队列
}
}else{
System.out.println("商品已抢购完毕-------");
}
}catch (Exception e){
e.printStackTrace();
}
}
}
package com.leftso.demo.demoredisseckill;
import com.leftso.demo.demoredisseckill.service.Seckill;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import redis.clients.jedis.JedisPool;
import java.util.ArrayList;
import java.util.List;
@RunWith(SpringRunner.class)
@SpringBootTest
public class RedisSeckillTest {
private final static Logger logger = LoggerFactory.getLogger(RedisSeckillTest.class);
@Autowired
JedisPool jedisPool;
String productKey="SSSSSSKEY";
int productNum=10;
@Before
public void before(){
jedisPool.getResource().set(productKey,10+"");//设置产品默认库存数量
while (jedisPool.getResource().lpop(productKey+"user")!=null){
}//清空秒杀成功人用户列表
//end
}
@After
public void after(){
String num=jedisPool.getResource().get(productKey);
System.out.println("剩余库存:"+num);
}
@Test
public void contextLoads() {
try {
for (int i = 0; i < 100; i++) {
//每个用户件数
Thread t = new Thread(new Seckill(jedisPool,"用户"+i,productKey));
t.start();
}
long size=jedisPool.getResource().llen(productKey+"user");
while (true){
if (size==productNum){
break;
}else{
size=jedisPool.getResource().llen(productKey+"user");
}
}
List<String> successUsers=new ArrayList<>();
String user=jedisPool.getResource().lpop(productKey+"user");
while (user!=null){
successUsers.add(user);
user=jedisPool.getResource().lpop(productKey+"user");
}
System.out.println("活动结束>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>活动结束");
System.out.println("获奖名单:"+successUsers);
Thread.currentThread().sleep(2000);
} catch (Exception e) {
e.printStackTrace();
}
}
}
..............................
商品已抢购完毕-------
商品已抢购完毕-------
商品已抢购完毕-------
商品已抢购完毕-------
商品已抢购完毕-------
商品已抢购完毕-------
商品已抢购完毕-------
商品已抢购完毕-------
商品已抢购完毕-------
商品已抢购完毕-------
商品已抢购完毕-------
商品已抢购完毕-------
商品已抢购完毕-------
商品已抢购完毕-------
商品已抢购完毕-------
商品已抢购完毕-------
活动结束>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>活动结束
获奖名单:[用户66, 用户2, 用户8, 用户10, 用户56, 用户78, 用户33, 用户58, 用户16, 用户87]
剩余库存:0
Process finished with exit code 0
https://www.leftso.com/article/615.html