:ambulance:redis队列超卖情况的分析以及解决方案

This commit is contained in:
zhipeng.zhang
2019-10-12 16:22:49 +08:00
parent bc8d0e7625
commit dcb60e0d3c
3 changed files with 87 additions and 69 deletions

View File

@@ -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], "秒杀失败");//推送给前台
}
}
} }

View File

@@ -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);
}
} }

View File

@@ -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>();