mirror of
https://gitee.com/52itstyle/spring-boot-seckill.git
synced 2025-12-30 10:22:26 +00:00
:ambulance:redis队列超卖情况的分析以及解决方案
This commit is contained in:
@@ -1,38 +1,40 @@
|
|||||||
package com.itstyle.seckill.queue.redis;
|
package com.itstyle.seckill.queue.redis;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import com.itstyle.seckill.common.entity.Result;
|
import com.itstyle.seckill.common.entity.Result;
|
||||||
import com.itstyle.seckill.common.enums.SeckillStatEnum;
|
import com.itstyle.seckill.common.enums.SeckillStatEnum;
|
||||||
import com.itstyle.seckill.common.redis.RedisUtil;
|
import com.itstyle.seckill.common.redis.RedisUtil;
|
||||||
import com.itstyle.seckill.common.webSocket.WebSocketServer;
|
import com.itstyle.seckill.common.webSocket.WebSocketServer;
|
||||||
import com.itstyle.seckill.service.ISeckillService;
|
import com.itstyle.seckill.service.ISeckillService;
|
||||||
/**
|
/**
|
||||||
* 消费者
|
* 消费者
|
||||||
* @author 科帮网 By https://blog.52itstyle.com
|
* @author 科帮网 By https://blog.52itstyle.vip
|
||||||
*/
|
*/
|
||||||
@Service
|
@Service
|
||||||
public class RedisConsumer {
|
public class RedisConsumer {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private ISeckillService seckillService;
|
private ISeckillService seckillService;
|
||||||
@Autowired
|
@Autowired
|
||||||
private RedisUtil redisUtil;
|
private RedisUtil redisUtil;
|
||||||
|
|
||||||
public void receiveMessage(String message) {
|
public void receiveMessage(String message) {
|
||||||
//收到通道的消息之后执行秒杀操作(超卖)
|
Thread th=Thread.currentThread();
|
||||||
String[] array = message.split(";");
|
System.out.println("Tread name:"+th.getName());
|
||||||
if(redisUtil.getValue(array[0])==null){//control层已经判断了,其实这里不需要再判断了
|
//收到通道的消息之后执行秒杀操作(超卖)
|
||||||
Result result = seckillService.startSeckilDBPCC_TWO(Long.parseLong(array[0]), Long.parseLong(array[1]));
|
String[] array = message.split(";");
|
||||||
if(result.equals(Result.ok(SeckillStatEnum.SUCCESS))){
|
if(redisUtil.getValue(array[0])==null){//control层已经判断了,其实这里不需要再判断了
|
||||||
WebSocketServer.sendInfo(array[0].toString(), "秒杀成功");//推送给前台
|
Result result = seckillService.startSeckilDBPCC_TWO(Long.parseLong(array[0]), Long.parseLong(array[1]));
|
||||||
}else{
|
if(result.equals(Result.ok(SeckillStatEnum.SUCCESS))){
|
||||||
WebSocketServer.sendInfo(array[0].toString(), "秒杀失败");//推送给前台
|
WebSocketServer.sendInfo(array[0], "秒杀成功");//推送给前台
|
||||||
redisUtil.cacheValue(array[0], "ok");//秒杀结束
|
}else{
|
||||||
}
|
WebSocketServer.sendInfo(array[0], "秒杀失败");//推送给前台
|
||||||
}else{
|
redisUtil.cacheValue(array[0], "ok");//秒杀结束
|
||||||
WebSocketServer.sendInfo(array[0].toString(), "秒杀失败");//推送给前台
|
}
|
||||||
}
|
}else{
|
||||||
}
|
WebSocketServer.sendInfo(array[0], "秒杀失败");//推送给前台
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,32 +1,48 @@
|
|||||||
package com.itstyle.seckill.queue.redis;
|
package com.itstyle.seckill.queue.redis;
|
||||||
|
|
||||||
import org.springframework.context.annotation.Bean;
|
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||||
import org.springframework.data.redis.listener.PatternTopic;
|
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||||
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
|
import org.springframework.data.redis.listener.PatternTopic;
|
||||||
import org.springframework.data.redis.listener.adapter.MessageListenerAdapter;
|
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
|
||||||
|
import org.springframework.data.redis.listener.adapter.MessageListenerAdapter;
|
||||||
@Configuration
|
|
||||||
public class RedisSubListenerConfig {
|
import java.util.concurrent.*;
|
||||||
//初始化监听器
|
|
||||||
@Bean
|
@Configuration
|
||||||
RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory,
|
public class RedisSubListenerConfig {
|
||||||
MessageListenerAdapter listenerAdapter) {
|
//初始化监听器
|
||||||
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
|
@Bean
|
||||||
container.setConnectionFactory(connectionFactory);
|
RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory,
|
||||||
container.addMessageListener(listenerAdapter, new PatternTopic("seckill"));
|
MessageListenerAdapter listenerAdapter) {
|
||||||
return container;
|
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
|
||||||
}
|
container.setConnectionFactory(connectionFactory);
|
||||||
//利用反射来创建监听到消息之后的执行方法
|
container.addMessageListener(listenerAdapter, new PatternTopic("seckill"));
|
||||||
@Bean
|
/**
|
||||||
MessageListenerAdapter listenerAdapter(RedisConsumer redisReceiver) {
|
* 如果不定义线程池,每一次消费都会创建一个线程,如果业务层面不做限制,就会导致秒杀超卖
|
||||||
return new MessageListenerAdapter(redisReceiver, "receiveMessage");
|
*/
|
||||||
}
|
ThreadFactory factory = new ThreadFactoryBuilder()
|
||||||
//使用默认的工厂初始化redis操作模板
|
.setNameFormat("redis-listener-pool-%d").build();
|
||||||
@Bean
|
Executor executor = new ThreadPoolExecutor(
|
||||||
StringRedisTemplate template(RedisConnectionFactory connectionFactory) {
|
1,
|
||||||
return new StringRedisTemplate(connectionFactory);
|
1,
|
||||||
}
|
5L,
|
||||||
|
TimeUnit.SECONDS,
|
||||||
|
new LinkedBlockingQueue<>(1000),
|
||||||
|
factory);
|
||||||
|
container.setTaskExecutor(executor);
|
||||||
|
return container;
|
||||||
|
}
|
||||||
|
//利用反射来创建监听到消息之后的执行方法
|
||||||
|
@Bean
|
||||||
|
MessageListenerAdapter listenerAdapter(RedisConsumer redisReceiver) {
|
||||||
|
return new MessageListenerAdapter(redisReceiver, "receiveMessage");
|
||||||
|
}
|
||||||
|
//使用默认的工厂初始化redis操作模板
|
||||||
|
@Bean
|
||||||
|
StringRedisTemplate template(RedisConnectionFactory connectionFactory) {
|
||||||
|
return new StringRedisTemplate(connectionFactory);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -55,7 +55,7 @@ public class SeckillPageController {
|
|||||||
return Result.ok(List);
|
return Result.ok(List);
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequestMapping("/startSeckill")
|
@PostMapping("/startSeckill")
|
||||||
public Result startSeckill(String ticket,String randstr,HttpServletRequest request) {
|
public Result startSeckill(String ticket,String randstr,HttpServletRequest request) {
|
||||||
HttpMethod method =HttpMethod.POST;
|
HttpMethod method =HttpMethod.POST;
|
||||||
MultiValueMap<String, String> params= new LinkedMultiValueMap<String, String>();
|
MultiValueMap<String, String> params= new LinkedMultiValueMap<String, String>();
|
||||||
|
|||||||
Reference in New Issue
Block a user