Spring Boot下Firebase通知:同一Token消息间隔5分钟发送实现
解决同一Token重复推送Firebase通知的问题
核心思路
针对每个设备Token记录最后一次推送通知的时间,每次触发推送前先判断当前时间与上次推送时间的间隔是否超过5分钟,只有满足条件才执行推送操作,并更新最后推送时间。
具体实现
1. 选择存储方式
- 单机部署:使用
ConcurrentHashMap存储Token和对应的最后推送时间,保证线程安全。 - 分布式部署:推荐用Redis,支持多实例共享状态。
2. 单机场景实现(基于ConcurrentHashMap)
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; // 全局缓存,存储Token与最后推送时间戳 private static final ConcurrentHashMap<String, Long> LAST_PUSH_TIME = new ConcurrentHashMap<>(); // 5分钟间隔,转换为毫秒 private static final long MIN_INTERVAL = TimeUnit.MINUTES.toMillis(5); public void sendChatNotification(User user){ String token = user.getToken(); if(token == null) { return; } long currentTime = System.currentTimeMillis(); // 获取上次推送时间,无记录则返回0 long lastPushTime = LAST_PUSH_TIME.getOrDefault(token, 0L); // 检查是否满足推送间隔 if(currentTime - lastPushTime >= MIN_INTERVAL) { try { Notification notification = Notification.builder() .setTitle("Title") .setBody("Body") .build(); Message message = Message.builder() .setToken(token) .setNotification(notification) .putData("payload", "logged-home") .build(); firebaseMessaging.send(message); // 更新最后推送时间 LAST_PUSH_TIME.put(token, currentTime); } catch (FirebaseMessagingException error) { throw new ApiRequestException("Error!"); } } }
3. 分布式场景实现(基于Redis)
import org.springframework.data.redis.core.StringRedisTemplate; import java.util.concurrent.TimeUnit; private final StringRedisTemplate redisTemplate; // 5分钟间隔 private static final long MIN_INTERVAL = 5; // Redis键前缀,避免键冲突 private static final String PUSH_TIME_KEY_PREFIX = "chat:push:last_time:"; // 通过构造函数注入StringRedisTemplate public YourService(StringRedisTemplate redisTemplate) { this.redisTemplate = redisTemplate; } public void sendChatNotification(User user){ String token = user.getToken(); if(token == null) { return; } String key = PUSH_TIME_KEY_PREFIX + token; String lastPushTimeStr = redisTemplate.opsForValue().get(key); long currentTime = System.currentTimeMillis(); // 无推送记录或已满足间隔条件时执行推送 if(lastPushTimeStr == null || currentTime - Long.parseLong(lastPushTimeStr) >= TimeUnit.MINUTES.toMillis(MIN_INTERVAL)) { try { Notification notification = Notification.builder() .setTitle("Title") .setBody("Body") .build(); Message message = Message.builder() .setToken(token) .setNotification(notification) .putData("payload", "logged-home") .build(); firebaseMessaging.send(message); // 更新Redis记录,设置1小时过期时间清理无效条目 redisTemplate.opsForValue().set(key, String.valueOf(currentTime), 1, TimeUnit.HOURS); } catch (FirebaseMessagingException error) { throw new ApiRequestException("Error!"); } } }
额外优化建议
- 单机场景下,可定时清理
ConcurrentHashMap中长时间未推送的Token记录,避免内存占用过高。 - 若需要合并间隔内的多条消息,可在缓存中暂存未推送的消息内容,间隔达标后合并成一条通知发送,提升用户体验。
内容的提问来源于stack exchange,提问作者Luis Rotoly
相关产品推荐
相关产品推荐

