摘要:好的,下面我将为你提供一个简单的示例代码,演示如何在 Spring Boot 中实现一个抢票系统,并利用分布式锁和队列进行处理。这个示例将使用 Redis 作为分布式锁的后端存储和队列的后端存储。
好的,下面我将为你提供一个简单的示例代码,演示如何在 Spring Boot 中实现一个抢票系统,并利用分布式锁和队列进行处理。这个示例将使用 Redis 作为分布式锁的后端存储和队列的后端存储。
首先,确保你的项目中已经引入了 redis 的依赖,通常可以使用 Spring Boot 提供的 spring-boot-starter-data-redis 来集成 Redis。
import org.springframework.beans.factory.annotation.Autowired;import org.springframework.data.redis.core.StringRedisTemplate;import org.springframework.stereotype.Service;@Servicepublic class TicketService {private static final String TICKET_QUEUE_KEY = "ticket_queue";private static final String TICKET_lock_KEY = "ticket_lock";@Autowiredprivate StringRedisTemplate redisTemplate;public Boolean tryToBookTicket(String userId) {// 利用分布式锁来控制抢票过程Boolean isLocked = redisTemplate.opsForValue.setIfAbsent(TICKET_LOCK_KEY, userId);if (isLocked != null && isLocked) {try {// 模拟抢票逻辑Thread.sleep(1000); // 模拟处理时间String ticket = redisTemplate.opsForList.leftPop(TICKET_QUEUE_KEY);if (ticket != null) {System.out.println("User " + userId + " booked ticket: " + ticket);return true;} else {System.out.println("User " + userId + " failed to book ticket, tickets sold out.");return false;}} catch (InterruptedException e) {e.printStackTrace;return false;} finally {// 释放分布式锁redisTemplate.delete(TICKET_LOCK_KEY);}} else {// 如果获取锁失败,则说明有其他用户正在抢票,返回失败System.out.println("User " + userId + " failed to book ticket, another user is booking.");return false;}}public void initTicketQueue(int totalTickets) {// 初始化票队列for (int i = 1; i抢票控制类
import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestParam;import org.springframework.web.bind.annotation.RESTController;@RestControllerpublic class TicketController {@Autowiredprivate TicketService ticketService;@GetMapping("/bookTicket")public String bookTicket(@RequestParam("userId") String userId) {if (ticketService.tryToBookTicket(userId)) {return "Successfully booked ticket.";} else {return "Failed to book ticket, please try again later.";}}}import org.springframework.boot.ApplicationArguments;import org.springframework.boot.ApplicationRunner;import org.springframework.stereotype.Component;import org.springframework.beans.factory.annotation.Autowired;@Componentpublic class ApplicationRunner implements ApplicationRunner {@Autowiredprivate TicketService ticketService;@Overridepublic void run(ApplicationArguments args) {// 初始化票队列ticketService.initTicketQueue(10); // 假设总共有 10 张票}}在这个示例中,TicketService 类负责处理抢票逻辑,其中 tryToBookTicket 方法利用 Redis 的分布式锁来控制抢票过程,并模拟了一个简单的抢票逻辑。TicketController 类是一个简单的 REST 控制器,用于接收用户请求来尝试抢票。ApplicationRunner 类用于在应用程序启动时初始化票队列。
请确保在配置文件中正确配置了 Redis 的连接信息。此外,你可能需要根据实际情况进行调整和优化。
我们可以在 TicketService 类中添加一些额外的功能,比如异步处理订单以及添加一个定时任务来处理过期订单。我们还可以使用 Redis 的消息队列来实现这些功能。
import org.springframework.beans.factory.annotation.Autowired;import org.springframework.data.redis.connection.Message;import org.springframework.data.redis.connection.MessageListener;import org.springframework.data.redis.core.RedisTemplate;import org.springframework.data.redis.core.StringRedisTemplate;import org.springframework.stereotype.Service;import java.util.concurrent.TimeUnit;@Servicepublic class TicketService implements MessageListener {private static final String TICKET_QUEUE_KEY = "ticket_queue";private static final String TICKET_LOCK_KEY = "ticket_lock";private static final String ORDER_QUEUE_KEY = "order_queue";@Autowiredprivate StringRedisTemplate redisTemplate;@Autowiredprivate RedisTemplate redisTemplateObject;public boolean tryToBookTicket(String userId) {// 利用分布式锁来控制抢票过程Boolean isLocked = redisTemplate.opsForValue.setIfAbsent(TICKET_LOCK_KEY, userId);if (isLocked != null && isLocked) {try {// 模拟抢票逻辑Thread.sleep(1000); // 模拟处理时间String ticket = redisTemplate.opsForList.leftPop(TICKET_QUEUE_KEY);if (ticket != null) {System.out.println("User " + userId + " booked ticket: " + ticket);// 异步处理订单redisTemplateObject.opsForList.rightPush(ORDER_QUEUE_KEY, userId + ":" + ticket);return true;} else {System.out.println("User " + userId + " failed to book ticket, tickets sold out.");return false;}} catch (InterruptedException e) {e.printStackTrace;return false;} finally {// 释放分布式锁redisTemplate.delete(TICKET_LOCK_KEY);}} else {// 如果获取锁失败,则说明有其他用户正在抢票,返回失败System.out.println("User " + userId + " failed to book ticket, another user is booking.");return false;}}public void initTicketQueue(int totalTickets) {// 初始化票队列for (int i = 1; i在这个示例中,我们添加了一个名为 ORDER_QUEUE_KEY 的 Redis 列表用于存储订单信息。tryToBookTicket 方法成功抢到票后会将订单信息放入订单队列中,表示用户已经成功预订了票。processExpiredOrders 方法是一个定时任务,它定期从订单队列中获取过期的订单并处理它们。
最后,我们需要在 Spring Boot 应用程序中配置 Redis 的消息监听器,以便监听订单消息:
ApplicationConfig.java
import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.data.redis.connection.RedisConnectionFactory;import org.springframework.data.redis.listener.ChannelTopic;import org.springframework.data.redis.listener.RedisMessageListenerContainer;@Configurationpublic class ApplicationConfig {@Beanpublic RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory, TicketService ticketService) {RedisMessageListenerContainer container = new RedisMessageListenerContainer;container.setConnectionFactory(connectionFactory);container.addMessageListener(ticketService, new ChannelTopic("order_channel"));return container;}}在这个配置中,我们创建了一个 RedisMessageListenerContainer,并将其连接到 Redis 的消息监听器 TicketService,订阅了名为 "order_channel" 的消息通道。
以上是一个简单的抢票系统的示例,使用了分布式锁和队列来保证系统的并发安全性和可靠性。请注意,这只是一个简单示例,实际应用中可能需要更多的功能和优化。
继续上面的示例,我们可以添加一个定时任务来处理过期订单,并且实现一个简单的接口来触发用户抢票操作。接下来,我们将完善这些功能:
TicketController.java (续)
import org.springframework.beans.factory.annotation.Autowired;import org.springframework.scheduling.annotation.Scheduled;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestParam;import org.springframework.web.bind.annotation.RestController;@RestControllerpublic class TicketController {@Autowiredprivate TicketService ticketService;@GetMapping("/bookTicket")public String bookTicket(@RequestParam("userId") String userId) {if (ticketService.tryToBookTicket(userId)) {return "Successfully booked ticket.";} else {return "Failed to book ticket, please try again later.";}}@Scheduled(fixedDelay = 5000) // 每隔5秒钟尝试处理过期订单public void processExpiredOrders {ticketService.processExpiredOrders;}}在这个更新后的 TicketController 中,我们添加了一个定时任务 processExpiredOrders 方法来处理过期订单。该定时任务将每隔5秒钟执行一次,尝试处理过期订单。
TicketService.java (续)
import org.springframework.beans.factory.annotation.Autowired;import org.springframework.data.redis.connection.Message;import org.springframework.data.redis.connection.MessageListener;import org.springframework.data.redis.core.RedisTemplate;import org.springframework.data.redis.core.StringRedisTemplate;import org.springframework.stereotype.Service;import java.util.concurrent.TimeUnit;@Servicepublic class TicketService implements MessageListener {private static final String TICKET_QUEUE_KEY = "ticket_queue";private static final String TICKET_LOCK_KEY = "ticket_lock";private static final String ORDER_QUEUE_KEY = "order_queue";@Autowiredprivate StringRedisTemplate redisTemplate;@Autowiredprivate RedisTemplate redisTemplateObject;public boolean tryToBookTicket(String userId) {// 利用分布式锁来控制抢票过程Boolean isLocked = redisTemplate.opsForValue.setIfAbsent(TICKET_LOCK_KEY, userId);if (isLocked != null && isLocked) {try {// 模拟抢票逻辑Thread.sleep(1000); // 模拟处理时间String ticket = redisTemplate.opsForList.leftPop(TICKET_QUEUE_KEY);if (ticket != null) {System.out.println("User " + userId + " booked ticket: " + ticket);// 异步处理订单redisTemplateObject.opsForList.rightPush(ORDER_QUEUE_KEY, userId + ":" + ticket);return true;} else {System.out.println("User " + userId + " failed to book ticket, tickets sold out.");return false;}} catch (InterruptedException e) {e.printStackTrace;return false;} finally {// 释放分布式锁redisTemplate.delete(TICKET_LOCK_KEY);}} else {// 如果获取锁失败,则说明有其他用户正在抢票,返回失败System.out.println("User " + userId + " failed to book ticket, another user is booking.");return false;}}public void initTicketQueue(int totalTickets) {// 初始化票队列for (int i = 1; i在这个更新后的 TicketService 类中,我们保留了之前的逻辑,并添加了一个用于定时处理过期订单的方法 processExpiredOrders。这个方法在定时任务中被调用,用于从订单队列中获取过期的订单并处理它们。
以上是一个完整的示例代码,实现了一个简单的抢票系统,并利用了分布式锁和队列来保证系统的并发安全性和可靠性。
来源:从程序员到架构师一点号