V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
wuzhizuiguo
V2EX  ›  Java

时光树洞的按时间发送顺序是怎么做的 ,rabbitmq 的"后入先出"

  •  
  •   wuzhizuiguo · 2019-12-05 10:47:28 +08:00 · 4017 次点击
    这是一个创建于 1807 天前的主题,其中的信息可能已经有所发展或是发生改变。
    看到有时光树洞, 选择一个时间点发送定时的邮件, 这种是保存到数据库,然后定时(每秒)查询数据库获取当时的邮件,然后发送?
    还是在用户提交后,写入数据库,写入消息队列,设置过期时间 (延时队列),最后根据时间先后顺序发送(但是队列是先入先出的)
    请问下大佬,这种是怎么做的?
    17 条回复    2019-12-08 17:28:06 +08:00
    skypyb
        1
    skypyb  
       2019-12-05 11:01:55 +08:00
    可以用时间轮
    rabbitmq 消息 TTL+死信也可以啊。
    wuzhizuiguo
        2
    wuzhizuiguo  
    OP
       2019-12-05 11:10:10 +08:00
    @skypyb 谢谢. rabbitmq 消息 TTL+死信队列 ,请问一下, 怎么实现 A 消息 10 秒过期, B 消息 5 秒过期, A 先进入队列, B 再进入, B 先出来发送邮件 . 博客上上说 , 队列进入时按照先入先出, 如果 B 过期了 还得等 A 过期了才会进入消费队列.
    skypyb
        3
    skypyb  
       2019-12-05 11:13:54 +08:00
    @wuzhizuiguo rabbitmq 可以设置每条消息的过期时间。不过不叫 ttl,具体参数叫啥搜索引擎搜一下吧
    wuzhizuiguo
        4
    wuzhizuiguo  
    OP
       2019-12-05 11:22:22 +08:00
    @skypyb 好的,谢谢. 是这个参数 message.getMessageProperties().setExpiration, 设置每条消息的过期时间. 现在如果每条消息设置同样的过期时间,A,B,C...都是 10 秒, 或者过期时间依次递增 10 秒,11 秒... 已经实现了. 会按过期时间顺序从死信队列里,然后进入 exchange 绑定的消费队列. 但是如果邮件发送时间 有 2 天后,1 天后,3 天后 这种 1 天后就得等 2 天后这条过期了,才会轮到它..
    FaceBug
        5
    FaceBug  
       2019-12-05 11:33:23 +08:00
    如果没有当天入洞,当天就要取出来的场景话,建议每天把次日的查出来,写到发送队列。

    不同 mq 有不同的机制,如果 MQ 本身没有排序功能,可以用 redis 的 zset 先按秒排序,然后入队列,甚至直接用 redis 的 zset,每秒提取已经到期的消息发送。
    wuzhizuiguo
        6
    wuzhizuiguo  
    OP
       2019-12-05 11:50:20 +08:00
    @cepczkd 谢谢. 如果没有当天写,当天就要发送的场景: 今天取出昨天的,查询排序,设置每条过期时间,写入延迟队列,发送,这个可行. 不过当天写 当天发送还是要有的..
    redis zet 这个是有新提交的,就重新写入,然后根据 score 值排序, 获取需要发送的消息. 这个可以
    mango88
        7
    mango88  
       2019-12-05 12:19:57 +08:00
    @wuzhizuiguo
    RabbitMQ 有个插件 rabbitmq_delayed_message_exchange,
    可以实现任意延时消息,而不同等待前面的消息过期,后面的消息才会过期这种情况
    mango88
        8
    mango88  
       2019-12-05 12:20:19 +08:00
    @mango88 不同 => 不用
    wuzhizuiguo
        9
    wuzhizuiguo  
    OP
       2019-12-05 17:26:24 +08:00
    @mango88 谢谢. 这个好.如果安装了 就可以通过设置过期时间来实现了. (就是要安装插件..)
    wuzhizuiguo
        10
    wuzhizuiguo  
    OP
       2019-12-05 17:29:42 +08:00
    暂时准备用把数据放到 zset 和 mysql 里, spring boot 里 跑一个间隔 1 秒的定时器, 根据 score 值排序 把等于或小于当前时间的数据取出来发送,(再删除这些发送过的)
    zhady009
        11
    zhady009  
       2019-12-05 21:02:37 +08:00
    redisson 有这种实现..还挺简单的 RScheduledExecutorService

    不过还是得用 mysql 记录一下 taskId
    wuzhizuiguo
        12
    wuzhizuiguo  
    OP
       2019-12-06 10:28:15 +08:00
    star7th
        13
    star7th  
       2019-12-06 11:18:38 +08:00
    两种方式都能做。用哪种方式都很轻松。第一种是起一个 cron 任务即可。用队列来做的话,利用我另一个开源项目 github.com/star7th/htq 里的定时任务特性就能做到。
    wuzhizuiguo
        15
    wuzhizuiguo  
    OP
       2019-12-08 14:52:47 +08:00
    @zhady009 好的,谢谢.我先用普通的定时器做做看,成功了再学下这个 redisson
    wuzhizuiguo
        16
    wuzhizuiguo  
    OP
       2019-12-08 14:54:31 +08:00
    @star7th 好的,谢谢,就是看到了答主的时光树洞.
    wuzhizuiguo
        17
    wuzhizuiguo  
    OP
       2019-12-08 17:28:06 +08:00
    已经确认可以按照过期时间发送邮件. 用的是普通前端提交参数(假设,我不会前端, 用的 postman),后台 mysql 储存信息, 同时写入 redis 的 hash 表和 zset 中,设置 score 值为过期时间.接着 spring boot 启动一个定时器,@Scheduled, 设置为上一次结束到到上一次任务开始为 1 秒间隔. 在其中读取 zset 中 0~当前时间的元素(邮件 id), 再去 hash 中找到对应邮件 id 的邮件具体信息,然后删除对应的记录, 接着直接写入消息队列,然后从消息队列里出来, RPC 调用邮件发送接口, 这个地方如果量大了,可以用线程池来 RPC 调用,因为之前其他地方出现过 Hystrix 默认的线程数错误问题.
    感谢各位大佬的热心解答,谢谢.
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2772 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 11:57 · PVG 19:57 · LAX 03:57 · JFK 06:57
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.