如何实现投票会话开启1分钟后自动将setIsSessionOpen设为FALSE?
投票会话自动关闭的实现方案
当前你的openSession方法仅完成了投票会话的开启逻辑,缺少1分钟后自动调用setIsSessionOpen(false)关闭会话的核心逻辑,以下是几种可行的实现方案:
方案1:Spring异步定时任务(轻量快捷)
如果是Spring项目,可利用@Async结合延迟任务实现,无需额外依赖:
代码实现
// 启动类需添加@EnableAsync注解开启异步支持 @Service public class SessionVoteService { private final SessionVoteRepository sessionVoteRepository; private final AgendaRepository agendaRepository; private final Logger LOGGER = LoggerFactory.getLogger(SessionVoteService.class); // 构造注入依赖 public SessionVote openSession(String agendaSubject, Integer sessionId){ SessionVote sessionVote = findBySessionVoteId(sessionId); Optional<Agenda> agenda = getAgendaBySubject(agendaSubject); if (agenda.isPresent()) { sessionVote.setAgenda(agenda.get()); sessionVote.setIsSessionOpen(true); sessionVote.setStartedTime(System.currentTimeMillis()); // 必须先保存会话状态到数据库 sessionVoteRepository.save(sessionVote); LOGGER.info("The session ID {} is opened and started at {}. The subject for voting is: {}", sessionVote.getSessionVoteId(), sessionVote.getStartedTime(), sessionVote.getAgenda().getSubject()); // 启动1分钟后关闭会话的异步任务 scheduleSessionClose(sessionId); return sessionVote; } else { LOGGER.error("No subject was found"); throw new NoSubjectFoundException(Constants.NO_SUBJECT_FOUND); } } @Async @Transactional void scheduleSessionClose(Integer sessionId) { try { // 延迟1分钟执行关闭逻辑 Thread.sleep(60000); SessionVote sessionVote = findBySessionVoteId(sessionId); if (sessionVote != null && sessionVote.getIsSessionOpen()) { sessionVote.setIsSessionOpen(false); sessionVoteRepository.save(sessionVote); LOGGER.info("Session ID {} has been automatically closed after 1 minute", sessionId); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); LOGGER.error("Failed to schedule session close for ID {}", sessionId, e); } } // 省略findBySessionVoteId、getAgendaBySubject等方法 }
注意事项
- 启动类必须添加
@EnableAsync注解 - 异步任务需添加
@Transactional保证数据库操作的一致性 - 避免主线程阻塞,任务逻辑需独立异步执行
方案2:Java原生ScheduledExecutorService(无框架依赖)
如果不想依赖Spring特性,可使用Java原生线程池实现延迟任务:
代码实现
@Service public class SessionVoteService { // 单线程定时任务池,可根据需求调整线程数 private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(); private final SessionVoteRepository sessionVoteRepository; // 其他依赖注入... public SessionVote openSession(String agendaSubject, Integer sessionId){ // 原有会话开启逻辑... sessionVoteRepository.save(sessionVote); // 提交延迟1分钟的关闭任务 scheduler.schedule(() -> closeSession(sessionId), 1, TimeUnit.MINUTES); return sessionVote; } @Transactional void closeSession(Integer sessionId) { SessionVote sessionVote = findBySessionVoteId(sessionId); if (sessionVote != null && sessionVote.getIsSessionOpen()) { sessionVote.setIsSessionOpen(false); sessionVoteRepository.save(sessionVote); LOGGER.info("Session ID {} closed automatically", sessionId); } } // 应用关闭时销毁线程池,避免资源泄漏 @PreDestroy void shutdownScheduler() { scheduler.shutdown(); } }
注意事项
- 需在应用关闭时调用
shutdown()销毁线程池 - 线程池大小可根据业务量调整,例如使用
newScheduledThreadPool(3)
方案3:数据库定时扫描(分布式场景适配)
如果是分布式系统,内存定时任务可能因节点故障丢失任务,可采用数据库+定时扫描的方案:
步骤1:扩展SessionVote实体
添加expireTime字段存储会话到期时间:
@Entity public class SessionVote { // 原有字段:votedYes、votedNo、isSessionOpen等 private long startedTime; private long expireTime; // 会话到期时间(毫秒级时间戳) }
步骤2:修改openSession方法
设置会话到期时间:
public SessionVote openSession(String agendaSubject, Integer sessionId){ SessionVote sessionVote = findBySessionVoteId(sessionId); Optional<Agenda> agenda = getAgendaBySubject(agendaSubject); if (agenda.isPresent()) { long startTime = System.currentTimeMillis(); sessionVote.setAgenda(agenda.get()); sessionVote.setIsSessionOpen(true); sessionVote.setStartedTime(startTime); sessionVote.setExpireTime(startTime + 60000); // 设置1分钟后到期 sessionVoteRepository.save(sessionVote); LOGGER.info("The session ID {} is opened and started at {}. The subject for voting is: {}", sessionVote.getSessionVoteId(), startTime, sessionVote.getAgenda().getSubject()); return sessionVote; } else { // 异常处理逻辑... } }
步骤3:编写定时扫描任务
每分钟检查并关闭到期会话:
@Service public class SessionCleanupService { private final SessionVoteRepository sessionVoteRepository; private final Logger LOGGER = LoggerFactory.getLogger(SessionCleanupService.class); // 构造注入依赖 @Scheduled(fixedRate = 60000) // 每分钟执行一次扫描 @Transactional public void closeExpiredSessions() { long currentTime = System.currentTimeMillis(); // 查询所有已到期且未关闭的会话 List<SessionVote> expiredSessions = sessionVoteRepository.findByIsSessionOpenTrueAndExpireTimeLessThan(currentTime); for (SessionVote session : expiredSessions) { session.setIsSessionOpen(false); sessionVoteRepository.save(session); LOGGER.info("Expired session ID {} closed", session.getSessionVoteId()); } } }
注意事项
- 启动类需添加
@EnableScheduling开启定时任务 - 可调整扫描频率(比如每10秒一次)提高实时性
- 适合分布式环境,避免单点故障导致的任务丢失
通用注意事项
- 并发校验:投票接口需额外校验会话状态(
isSessionOpen)和当前时间是否在有效期内,避免延迟任务未执行完导致的无效投票 - 事务一致性:所有修改会话状态的操作需添加
@Transactional注解 - 日志监控:完善会话开启、关闭、投票的日志记录,方便问题排查
内容的提问来源于stack exchange,提问作者dcm50
相关产品推荐
相关产品推荐

