企业微信机器人怎么发定时消息?延迟发送队列的设计与实现
"明天早上九点给这个群发会议提醒"——定时消息是企微机器人里需求量排前三的功能。接口本身是即时发送,定时能力要自己搭:延迟队列、到期触发、取消机制,三件齐了功能才完整。这篇讲一个能跑生产环境的定时发送设计。
背景痛点
第一版定时消息通常长这样:定时任务扫表,到时间的行调发送接口。能用,但三个问题马上出现:扫描间隔决定误差,扫得勤了空转、扫得懒了晚发;取消只能靠改状态,改完还在旧一轮扫描里被捞出来;重启后"刚好卡在发送瞬间"的任务,发没发过没人说得清。
原理
所谓定时消息,是指把发送请求挂起至指定时刻再执行的机制,工程实现是一个带时间索引的任务队列:提交时任务落库并获得唯一标识,调度器按到期时间近的优先逐个触发,取消按标识撤销,执行结果回写状态。
设计上分清两种类型:定时消息(指定时刻发一次)用到期扫描即可;延迟消息(从现在起 N 分钟后发)更适合延迟队列中间件。两者都要有取消,没有取消的定时消息是不完整的产品——客户临时取消会议,提醒照发,体验就崩了。
核心实现
调度循环与取消(示例代码,需按官方文档校验):
def scheduler_loop():
# 示例代码,需按官方文档校验
while True:
for task in repo.due_tasks(now()): # 到期任务,按时间序
if repo.claim(task.id): # 抢占式领取,防重复执行
if task.cancelled: continue
api("/wx-api/api/message/sendText", task.payload)
repo.mark_sent(task.id)
time.sleep(5) # 误差容忍度按业务定
def cancel_task(task_id, appid) -> bool:
# 示例代码,需按官方文档校验
return repo.cancel_before_claim(task_id, appid)
取消的实现要点是"领取前可撤、领取后不可撤":任务被调度器领取的瞬间状态翻转,取消请求只在领取前生效,返回值必须告诉用户"取消成功"还是"太晚了已发出"。发送接口细节见消息模块文档,账号实例的调度隔离见实例文档。
踩坑
一、claim(领取)必须是原子操作,多实例部署时两个调度器同时领到同一个任务,就是两次提醒。数据库的原子更新或分布式锁二选一,别用"先查再改"。
二、重启瞬间的任务状态要靠落库恢复:调度器无状态化,任务状态只在库里,重启后接着扫,不丢不重。
三、到期时间用标准时区存储,展示层再做本地化;"服务器时区和用户时区差八小时"这类事故在跨国团队里是常客。
四、定时文案在提交时做校验和快照,到期时直接发——等到期再渲染模板,中间模板改了,用户收到的是改后内容,和当初确认的不一致。
总结
定时消息三件套:时间索引队列、原子化领取、领取前可取消。再补上文案快照和时区纪律,这套东西就稳了。会议提醒、生日关怀、周期报表都建立在它上面。这篇基于 Eyun 平台的企微接口整理,发送接口的频率限制以官方文档为准,调度器上线前用双实例演练一次领取竞争。
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐



所有评论(0)