package com.ard.gb28181.task.deviceSubscribe.deviceSubscribe; import com.ard.gb28181.api.bean.SipTransactionInfo; import com.ard.gb28181.config.UserSetting; import com.ard.gb28181.utils.redis.RedisUtil; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import java.time.Duration; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.DelayQueue; import java.util.concurrent.TimeUnit; @Slf4j @Component public class SubscribeTaskRunner { private final Map subscribes = new ConcurrentHashMap<>(); private final DelayQueue delayQueue = new DelayQueue<>(); @Autowired private RedisTemplate redisTemplate; @Autowired private UserSetting userSetting; private final String prefix = "VMP_DEVICE_SUBSCRIBE"; // 订阅过期检查 @Scheduled(fixedDelay = 500, timeUnit = TimeUnit.MILLISECONDS) public void expirationCheck() { // 1. 使用 poll() 替代 take() // poll() 会立即返回:如果有已超期的任务则返回,否则返回 null SubscribeTask take = delayQueue.poll(); // 2. 循环处理所有已超期的任务 while (take != null) { try { removeSubscribe(take.getKey()); take.expired(); } catch (Exception e) { log.error("[设备订阅到期] {} 到期处理时出现异常, 设备编号: {} ", take.getName(), take.getDeviceId()); } // 3. 继续尝试获取下一个已超期的任务 take = delayQueue.poll(); } // 4. 如果 poll() 返回 null,说明当前没有超期的任务,方法直接结束 // 线程释放,等待下一次 500ms 调度 } public void addSubscribe(SubscribeTask task) { Duration duration = Duration.ofSeconds((task.getDelayTime() - System.currentTimeMillis()) / 1000); if (duration.getSeconds() < 0) { return; } subscribes.put(task.getKey(), task); String key = String.format("%s_%s_%s", prefix, userSetting.getServerId(), task.getKey()); redisTemplate.opsForValue().set(key, task.getInfo(), duration); delayQueue.offer(task); } public boolean removeSubscribe(String key) { SubscribeTask task = subscribes.get(key); if (task == null) { return false; } String redisKey = String.format("%s_%s_%s", prefix, userSetting.getServerId(), task.getKey()); redisTemplate.delete(redisKey); subscribes.remove(key); if (delayQueue.contains(task)) { boolean remove = delayQueue.remove(task); if (!remove) { log.info("[移除订阅任务] 从延时队列内移除失败: {}", key); } } return true; } public SipTransactionInfo getTransactionInfo(String key) { SubscribeTask task = subscribes.get(key); if (task == null) { return null; } return task.getTransactionInfo(); } public boolean updateDelay(String key, long expirationTime) { SubscribeTask task = subscribes.get(key); if (task == null) { return false; } log.info("[更新订阅任务时间] {}, 编号: {}", task.getName(), key); delayQueue.remove(task); task.setDelayTime(expirationTime); delayQueue.offer(task); String redisKey = String.format("%s_%s_%s", prefix, userSetting.getServerId(), task.getKey()); Duration duration = Duration.ofSeconds((expirationTime - System.currentTimeMillis()) / 1000); redisTemplate.expire(redisKey, duration); return true; } public boolean containsKey(String key) { return subscribes.containsKey(key); } public List getAllTaskInfo() { String scanKey = String.format("%s_%s_*", prefix, userSetting.getServerId()); List values = RedisUtil.scan(redisTemplate, scanKey); if (values.isEmpty()) { return new ArrayList<>(); } List result = new ArrayList<>(); for (Object value : values) { String redisKey = (String) value; SubscribeTaskInfo taskInfo = (SubscribeTaskInfo) redisTemplate.opsForValue().get(redisKey); if (taskInfo == null) { continue; } Long expire = redisTemplate.getExpire(redisKey, TimeUnit.SECONDS); taskInfo.setExpireTime(expire); result.add(taskInfo); } return result; } }