Py学习  »  Python

Python 实现公众号定时提醒,再也不会忘记待办工作了

编程派 • 4 年前 • 513 次点击  

定时任务是业务中很常见的一个需求。本文作者通过某公众号任务提醒的项目,介绍了如何实现用户定时提醒。

文 | 喵叔 原文:https://blog.betacat.io/post/how-wecron-schedules/

WeCron(微定时)是我开发的一个微信上的定时提醒机器人,它能解析用户输入的语音或者文字,提取其中的时间和事件信息,然后为用户设置提醒。这个服务上线后,经常有用户问我这里定时提醒的实现,因此这篇文章我就打算来谈谈WeCron是怎么实现这个功能的,希望大家以后设计定时调度类系统时能多一个参考。


通过微信设置定时提醒

架构

下面是定时提醒部分的架构图:

WeCron定时功能架构图

整个流程还是比较简单的:

  1. 用户输入一条提醒语音后, parser会把其中的时间和事件信息解析出来,存到数据库中;

  2. 同时,定时调度器会被触发,它到数据库中找到一个最近的提醒,计算出到当前时间的时间差,然后sleep相应的秒数;

  3. 过段时间,调度器醒来,它会将最近一段时间要发送的提醒任务提交到一个队列里面,由专门发送消息的worker向用户推送消息。这里使用队列的目的,一方面是消峰,另一方面是为了让发送消息的worker能够横向扩展。

定时调度器

定时提醒类系统有一个特点,就是它的峰值特别明显。用户设置的提醒大多会集中在整点时间触发,尤其是早晨8、9点。面对这样一个不均衡的分布,固定时间轮训的调度法就显得有些naïïve,它很难在运行效率和提醒的及时性中找到一个平衡点。因此,WeCron中定时调度器被设计为一个事件循环,它能响应两种事件:

  1. 数据库有更新或者插入操作

  2. 上一次设置的休眠时间到了

每当这个调度器被唤醒,它会首先检查有没有提醒要发送,然后再找到最近一个将要触发的提醒,休眠相应的时间。下面是一个简短的实现:

  1. remind_event = threading.Event()


  2. def event_loop():

  3. while True:

  4. wait_seconds = process_jobs()

  5. # This event wakes up on timeout or someone else sets it

  6. remind_event.wait(wait_seconds)


  7. def on_remind_update():

  8. # This will wake up the remind_event

  9. remind_event.set()


  10. def process_jobs():

  11. # Send reminds

  12. reminds = get_unfired_reminds_older_than_now()

  13. send_notification(reminds)


  14. # And get the most recent remind

  15. next_remind = get_unfired_reminds_newer_than_now().limit(1)

  16. return (next_remind.notify_time - now).seconds

另外,为了提高提醒的及时性,这里的调度器会被启动好几份。这时面临的一个问题就是同一个提醒可能会被多个调度器调度到,用户也就有可能重复收到提醒。所以WeCron在选择提醒项的时候使用了 select...forupdate语句,利用(行)锁来同步多个调度器。

结束

好了,我就先简单的介绍到这里,希望可以帮助到有需要的人。如果想要看到具体实现,请参考WeCron的代码库。

公众号后台回复关键词【wecron】,即可获取代码链接。

回复下方「关键词」,获取优质资源


回复关键词「 pybook03」,立即获取主页君与小伙伴一起翻译的《Think Python 2e》电子版

回复关键词「入门资料」,立即获取主页君整理的 10 本 Python 入门书的电子版

回复关键词「m」,立即获取Python精选优质文章合集

回复关键词「book 数字」,将数字替换成 0 及以上数字,有惊喜好礼哦~


推荐阅读



题图:pexels,CC0 授权。

Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/49671
 
513 次点击