缘由
rabbitMQ消息中间件的简单使用。 基于削峰原理,设计一个抢豆子的小游戏,服务器中设有100颗豆子,所有用户均可进行抢夺,用户的请求被缓存放入消息队列中,豆子被抢光或游戏时间耗尽,游戏结束。
效果图
步骤
1.下载安装并开启rabbitMQ 2.springboot集成rabbitMQ并配置参数 3.本例中以redis作为共享内存放置数据 4.配置监听器,在程序开启后,初始化游戏信息(豆子数量、用户成绩、游戏状态) 5.设置定时任务,每十分钟开启游戏,在第9分钟时,关闭游戏。 6.编写生产者,即抢豆子的请求。 7.编写消费者,即维护redis中的游戏数据
代码
/**
* 自定义rabbitMQ的配置
* 设置队列,交换机
* /
@Configuration
@EnableRabbit
public class RabbitMQConfig {
public static final String DIRECT_EXCHANGE_1 = "DIRECT_EXCHANGE_1"; // 自定义交换机
public static final String QUEUE_1 = "QUEUE_1"; // 自定义队列
public static final String ROUTING_KEY_1 = "ROUTING_KEY_1"; // 自定义路由
@Value("${spring.rabbitmq.host}")
private String host;
@Value("${spring.rabbitmq.port}")
private int port;
@Value("${spring.rabbitmq.username}")
private String username;
@Value("${spring.rabbitmq.password}")
private String password;
//建立一个连接容器,类型数据库的连接池
@Bean
public ConnectionFactory connectionFactory() {
CachingConnectionFactory connectionFactory = new CachingConnectionFactory(host,port);
connectionFactory.setUsername(username);
connectionFactory.setPassword(password);
connectionFactory.setVirtualHost("/");
connectionFactory.setPublisherConfirms(true);
return connectionFactory;
}
// RabbitMQ的使用入口
@Bean
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
//必须是prototype类型
public RabbitTemplate rabbitTemplate() {
RabbitTemplate template = new RabbitTemplate(this.connectionFactory());
template.setMessageConverter(this.jsonMessageConverter());
template.setMandatory(true);
return template;
}
/**
* 定义消息转换实例 ,转化成 JSON传输
* @return
*/
@Bean
public MessageConverter jsonMessageConverter() {
return new Jackson2JsonMessageConverter();
}
/**
* 定义交换机
*/
@Bean
public DirectExchange defaultExchange() {
return new DirectExchange(DIRECT_EXCHANGE_1);
}
/**
* 自定义队列
*/
@Bean
public Queue queue_1() {
return new Queue(QUEUE_1, true); //队列持久
}
/**
* 交换机与队列绑定
*
* 一个交换机可以绑定多个队列
* 有很多类交换机,他们绑定队列的规则是不同的
* https://blog.csdn.net/qq_35387940/article/details/100514134
*/
@Bean
public Binding binding() {
return BindingBuilder.bind(queue_1()).to(defaultExchange()).with(RabbitMQConfig.ROUTING_KEY_1);
}
}
/**
* 监听springboot启动成功后事件
*/
@Component
public class SpringListener implements CommandLineRunner {
@Autowired
private TRedisCache tRedisCache;
/**
* 抢豆子模块初始化redis中的数据
*/
private void initRedisBeansData(){
// 初始化豆子数量
tRedisCache.setCacheObject(GrabBeansRedisConfig.NUM_KEY,GrabBeansRedisConfig.BEANS_NUMBER);
// 初始化成绩
tRedisCache.deleteObject(GrabBeansRedisConfig.RECORD_KEY);
tRedisCache.setCacheList(GrabBeansRedisConfig.RECORD_KEY,null);
// 初始化游戏状态为0
tRedisCache.setCacheObject(GrabBeansRedisConfig.STATUS_KEY,1);
}
@Override
public void run(String... args) throws Exception {
this.initRedisBeansData();
}
}
/**
* 定时任务配置
*
* @author ruoyi
*/
@Configuration
@EnableScheduling //开启定时任务
public class ScheduleConfig
{
@Autowired
private TRedisCache tRedisCache;
/**
* 抢豆子模块的定时任务
* 定时重置redis中的豆子数量及成绩等信息
*/
@Scheduled(cron = "0 8,18,28,38,48,58 * * * ?")
//每小时的第 8,18,28,38,48,58分的时候触发执行
public void reset() {
// 重置豆子数量
tRedisCache.setCacheObject(GrabBeansRedisConfig.NUM_KEY,GrabBeansRedisConfig.BEANS_NUMBER);
// 重置游戏状态为0
tRedisCache.setCacheObject(GrabBeansRedisConfig.STATUS_KEY,0);
}
/**
* 抢豆子定时任务-游戏开始
* @return
*/
@Scheduled(cron = "0 0,10,20,30,40,50 * * * ?")
public void start(){
// 重置成绩
tRedisCache.setCacheList(GrabBeansRedisConfig.RECORD_KEY,null);
// 变更游戏状态
tRedisCache.setCacheObject(GrabBeansRedisConfig.STATUS_KEY,1);
//
System.out.println("执行了start定时任务");
}
}
/**
* 设置生产者
* /
@RestController
@RequestMapping("/grabBeansProducer")
public class GrabBeansProducer {
@Autowired
private RabbitTemplate rabbitTemplate;
@Autowired
private TRedisCache tRedisCache;
@Autowired
private TokenService tokenService;
/**
* 抢豆子
* @return
*/
@GetMapping("/do")
public AjaxResult grab(){
// 判断游戏是否开始
Object status = tRedisCache.getCacheObject(GrabBeansRedisConfig.STATUS_KEY);
if(status==null || Integer.parseInt(status.toString())==0){
return AjaxResult.error("本场游戏已结束");
}
//获取用户信息
LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
String userName = loginUser.getUser().getUserName();
// 发布消息
rabbitTemplate.convertAndSend(RabbitMQConfig.DIRECT_EXCHANGE_1, RabbitMQConfig.ROUTING_KEY_1, userName);
return AjaxResult.success();
}
/**
* 获取本场成绩
*/
@GetMapping("/get")
public AjaxResult getRecord(){
List<Object> record = tRedisCache.getCacheList(GrabBeansRedisConfig.RECORD_KEY);
return AjaxResult.success(record);
}
}
/**
* 设置消费者
* /
@Component
@RabbitListener(queues = RabbitMQConfig.QUEUE_1)
public class GrabBeansCustomer {
@Autowired
private TRedisCache tRedisCache; //这个其实就是redisTemplate的自定义封装
@RabbitHandler
@Transactional //这里对redis的操作开启了事务
public void process(String userName) {
// 判断是否已结束
Object status = tRedisCache.getCacheObject(GrabBeansRedisConfig.STATUS_KEY);
if(status==null || Integer.parseInt(status.toString())==0)
return;
// 获取现有豆子数量
Object numObj = tRedisCache.getCacheObject(GrabBeansRedisConfig.NUM_KEY);
int num = Integer.parseInt(numObj.toString());
// 获取成绩
List<Map<String,Object>> record = tRedisCache.getCacheList(GrabBeansRedisConfig.RECORD_KEY);
boolean in = false;
for (Map<String,Object> m: record
) {
String user = m.get("user").toString();
if(userName.equals(user)){
in = true;
int grade = Integer.parseInt(m.get("grade").toString());
// 分数加1
m.put("grade",++grade);
// 豆子数量减1
tRedisCache.setCacheObject(GrabBeansRedisConfig.NUM_KEY,--num);
// 如果豆子数量为0,游戏结束
if(num==0)
tRedisCache.setCacheObject(GrabBeansRedisConfig.STATUS_KEY,0);
}
}
//如果用户不在成绩单中,添加进去
if(in==false){
// 添加成绩
HashMap<String, Object> map = new HashMap<>();
map.put("user",userName);
map.put("grade",1);
record.add(map);
// 豆子数量减1
tRedisCache.setCacheObject(GrabBeansRedisConfig.NUM_KEY,--num);
// 如果豆子数量为0,游戏结束
if(num==0)
tRedisCache.setCacheObject(GrabBeansRedisConfig.STATUS_KEY,0);
}
// 删除原有成绩
tRedisCache.deleteObject(GrabBeansRedisConfig.RECORD_KEY);
// 重新上传成绩
tRedisCache.setCacheList(GrabBeansRedisConfig.RECORD_KEY,record);
}
}