springboot基于rabbitMQ削峰原理实现抢豆子小游戏

tech2025-12-02  22

缘由

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); } }
最新回复(0)