在这个项目中,每个人都会有"计划任务"还有"超时"需求,在初始阶段,我们基本上用几个定时器。甚至当工作量越来越大的时候,我们也不可避免的会维护大量的定时器或者进行大量低效的扫描。
定时任务使用场景:在订单仍未支付的情况下,如何及时关闭订单(已使用)?
如何定期检查处于退款状态的订单是否已成功退款(以便后期重建)
设计方案:
整个Redis作为消息池,以KV形式存储的消息使用ZSET作为优先级队列,按照Score维护的优先级使用链表结构。先入先出模式用于消耗ZSET和链表存储消息地址(对应消息池的每个键),定时器用于维护路由实现消息按TTL规则延时。
RoomWits在这个阶段使用了这一组方法:
1.添加一个新的作业会在job_pool中插入一段数据,记录业务端和消费者端。还将在bucket中插入一条记录来记录执行的时间戳。
2.处理线程会到桶中找出哪些执行时间戳的RunTimeMillis小于当前时间,并删除所有这些记录;同时会对每个任务的主题进行分析,然后将这些任务推送到主题对应的列表队列中。
3每个话题的列表都会有一个监控线程批量获取列表中要消费的数据,所有获取的数据都会被扔进这个话题的消费线程池中。
4.消费者线程池执行会去job_pool找数据结构,返回给回调结构,执行回调方法。
要优化的内容:
目前只有一个队列存储消息,当大量需要消费的消息堆积在一起时,会影响消息通知的及时性。改进方法是打开多个队列,路由消息,然后打开多个消费线程进行消费。提供吞吐量的消息没有被持久化,因此存在消息稍后被持久化到MongoDB中的风险。
一般来说,还有哪些途径可以实现这种需求?
"轮询扫描方法"
1.使用映射<>记录每个uid最后请求时间last_packet_time。
2.当用户uid有请求包时,实时更新这个映射。
3.启动一个定时器,当它不在Map中空时,轮询扫描Map,看每个uid的last_packet_time是否超过30s,如果是,超时。
“多计时器触发方法”
1.使用映射<>记录每个uid最后请求时间last_packet_time。
2.当一个用户的uid请求包到达时,实时更新这个地图,同时为这个uid请求包启动一个定时器,30s后触发。
3.每个uid请求包对应的定时器被触发后,在Map中查找该uid的last_packet_time是否超过30s,如果是,则进行超时处理。
方案一:只启动一个定时器,但需要轮询,效率低。
方案二:不需要轮询,但每个请求包要启动一个timer,比较耗资源
本文来自少年我念你投稿,不代表舒华文档立场,如若转载,请注明出处:https://www.chinashuhua.cn/24/649969.html