延时通知解决方案

前言

这段时间项目中遇到了客户提出的新的功能,需要当用户参加志愿者活动的时候,在改活动开始的前5分钟和后10分钟推送一条微信消息到用户的微信上。其实这样的延时操作的场景还有很多,比如常见的电商系统里面的30分钟订单为支付就关闭这种功能。

然后我进行技术方案的调研,发现目前能满足该业务场景方案主要有以下几种:

  • 轮询数据库表 ,构建消息之后存放到数据库中,然后开启一个每分钟执行定时任务扫描数据库表中,活动开始时间在当前时间的后5分钟和活动的结束时间在当前时间的后10分钟。这种方式实现简单,但是对数据库的压力较大(最次)
  • 基于JDK中的DelayQueue,Java中自带了一个延时队列的功能,通过实现Delayd接口可以实现自定义的延时逻辑,非常简单。但是消息数据没有吃就会,当发生了故障宕机了之后,这些消息就不存在了。不过可以考虑将消息持久化到数据库中,同时使用延时队列,当消费成功之后删除数据库中的消息。如果应用宕机了,在重启的时候将满足时间要求的消息重新投放到延时队列中。(较次)
  • 基于Redis的Key过期通知,用户订阅消息之后,将消息存放到Redis中然后设置Key的过期时间,服务端开启一个线程监听Redis Key过期事件的回调。这种方式实现简单,利用Redis本身提供过的特性,但是Key过期的回调中并不能获取到该Key对应的Value,所以还需要在Redis中冗余一份Key+“特殊字符”->Value映射的一个键值对,这样对导致不必要的键值对存在。(较次)
  • 基于RabbitMQ的延时队列,RabbitMQ通过一个普通队列和一个死信队列可以实现一个延时队列的功能,首先将消息设置一个过期时间投放到普通队列中,这个普通队列没有消费者,那么当消息过期后该消息将会被转移到死信队列中,开启一个消费者消费死信队列中的数据即可;默认RabbitMQ是不支持延时队列的,不过提供了延时队列的插件可以集成到RabbitMQ,集成的过程非常简单,百度一下,你就知道哦。(好)

下面我将会把上面的四种方式都实现一遍(除了第一种),然后各位体会一下不同实现的方式的优越点

各种实现

基于JDK的DelayQueue

public class DelayedMessage implements Delayed {
   
    //微信的用户Id
    private final String openId;
    //活动名称
    private final String activityName;
    private final Long expireTime;

    public DelayedMessage(String openId, String activityName, long expireTime) {
   
        this.openId = openId;
        this.activityName = activityName;
        this.expireTime = expireTime;
    }

    @Override
    public long getDelay(TimeUnit unit) {
   
        return unit.convert(expireTime, TimeUnit.NANOSECONDS) - unit.convert(System.currentTimeMillis(), TimeUnit.NANOSECONDS);
    }

    @Override
    public int compareTo(Delayed o) {
   
        DelayedMessage message = (DelayedMessage) o;
        return this.expireTime.compareTo(message.getExpireTime());
    }

    public String getOpenId() {
   
        return openId;
    }

    public String getActivityName() {
   
        return activityName;
    }

    public Long getExpireTime() {
   
        return expireTime;
    }

    public static void main(String[] args) throws InterruptedException {
   
        DelayQueue<DelayedMessage> delayQueue = new DelayQueue<>();
        //投放消息线程
        new Thread(() -> {
   
            DelayedMessage message1 = new DelayedMessage("微信用户1", "打扫门前雪活动",
                                                         System.currentTimeMillis() + 1000 * 10); //10S后过期
            delayQueue.put(message1);
            System.out.println("投放消息:" + message1 + " 投放时间:" + LocalDateTime.now());

        }).start();
        //消费消息线程
        new Thread(() -> {
   
            while (!Thread.currentThread().isInterrupted()) {
   
                DelayedMessage delayedMessage = null;
                try {
   
                    delayedMessage = delayQueue.take();
                } catch (InterruptedException e) {
   
                    e.printStackTrace();
                }
                System.out.println("获取到消息:" + delayedMessage 
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
如果预测的数据具有延时性,即未来的观测值受到过去观测值的影响,可以考虑以下几种方法来解决延时性问题: 1. 特征滞后:将过去时刻的观测值作为特征输入到模型中。通过将时间步长较早的观测值作为额外的特征输入,模型可以捕捉到过去观测值对未来观测值的影响。可以通过调整滞后的时间步长来控制模型对过去观测值的考虑程度。 2. 窗口方法:将一段连续的过去观测值作为一个窗口,作为模型的输入。例如,可以使用滑动窗口的方式,每次将一定长度的过去观测值作为输入,以预测下一个时刻的观测值。通过调整窗口大小和滑动步长,可以控制模型对过去观测值的考虑范围。 3. 序列模型:使用递归神经网络(如LSTM、GRU)或卷积神经网络(如1D卷积)等序列模型来建模延时性。这些模型具有记忆机制,能够捕捉到序列数据中的依赖关系和趋势。通过将过去的观测序列作为模型的输入,可以让模型学习到观测值之间的延时性关系。 4. 时间特征工程:除了直接使用过去观测值作为特征外,还可以通过提取时间特征来捕捉延时性。例如,可以提取观测时间的小时、星期几、季节等特征,并将其作为模型的输入。这样模型能够根据时间特征来学习到观测值的延时性。 需要根据具体问题和数据特点选择合适的方法。可以尝试不同的方法,并进行实验和评估,以找到最合适的解决方案

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值