MailCheap如何把20万封邮件铺开到几个小时?QStash批量发送架构揭秘 【免费下载链接】mailcheapNewsletters where you pay for the emails you send, not for dormant subscribers, at a fraction of the price of beehiiv and the like. Open source: self-host it, or use mailcheap.ai.项目地址https://gitcode.com/gh_mirrors/mai/mailcheap点击查看免费下载MailCheap 是一款开源的 Newsletter 邮件营销平台主打“只为发出的邮件付费不为沉睡订阅者付费”的按量计费模式。当你点击发送一封有 20 万收件人的活动时MailCheap 并不会一股脑把邮件塞给邮件服务商而是借助 Upstash QStash 把发送任务切成 4000 个小批次按计算好的时间轴均匀铺开再配合限流、重试与首批发放保护稳稳地铺开到几个小时里。这篇文章带你拆穿这套批量发送架构的每一个关键零件。一、为什么 20 万封邮件不能一次性发出直接全速发送 20 万封邮件几乎必然踩雷触发服务商限流SES 等邮件服务商有账号级发送速率上限超限就返回ThrottlingException整批邮件被拒损伤发送域名信誉短时间大量陌生邮箱收到你的邮件硬反弹率飙升轻则被暂停发送重则整个账号被封新域名尤其脆弱冷启动的发送域名需要“热身”才能逐步提升配额。所以核心问题不是“能不能发完”而是**“以什么节奏发完”**。MailCheap 的答案是把发送变成一个可计算、可恢复的分布式流水线。二、总览从点击“发送”到邮件出栈整个流程可以概括为五步建消息为每个收件人生成一条Message记录状态queued批量写入数据库算节奏根据账号历史发送量算出本次发送应铺开的总时长切批次每50 封为一个批次SEND_BATCH_SIZE20 万封就是 4000 个批次每个批次是一个独立的 QStash 任务定时投递QStash 按notBefore时间逐个把批次“唤醒”到/api/jobs/send-batch接口逐封出栈批次内逐封检查限流、抑制名单、套餐额度交给邮件服务商发送并记录结果。三、批次化200000 ÷ 50 4000 个 QStash 任务切批的核心在 lib/sending.js 的publishBatches函数用chunk(messageIds, SEND_BATCH_SIZE)把消息 ID 切成每批 50 个每个批次对应一个指向/api/jobs/send-batch的 QStash 任务body 只带accountId和messageIds任务用client.batchJSON以 100 个一组发布到 QStash避免发布本身成为瓶颈所有批次共享同一个流控键{ key: account-accountId, parallelism: 1 }同一账号任意时刻只有一个批次在跑多个账号的批次则并行执行。接收端 app/api/jobs/send-batch/route.js 做了严格校验只接受 1 到 100 个MAX_BATCH_MESSAGES合法的消息 IDmaxDuration 300保证 Serverless 函数有 5 分钟处理窗口。四、铺开算法发送时长是怎么算出来的“铺开到几个小时”的时长不是拍脑袋而是 lib/send-spread.js 里的一套公式配合 lib/config.js 中的WARMUP配置参数值含义quickEmails200不超过 200 封直接全速发days30账号从首次活动起进入 30 天热身期growth2 → 5热身第 1 天可全速发“已证明量”的 2 倍第 30 天起为 5 倍minHours4需要铺开时基础时长 4 小时hoursPerDoubling4每超出配额一倍再加 4 小时maxHours72最久铺开 72 小时“已证明量”proven volume指该账号最近 60 天内、反弹与投诉率健康、且至少发出去一天的最大单场活动量。例如一个已证明量为 10 万、热身第 30 天的账号发 20 万封配额 100000 × 5 500000不对——若已证明量只有 2 万配额 20000 × 5 10000020 万 ÷ 10 万 2 倍 → 时长 4 4×log₂(2) 8 小时。随后getBatchStarts把 4000 个批次的启动时间均匀分布第一批立即执行最后一批在结束时刻中间的批次等间隔启动。QStash 的notBefore延迟投递让每个批次“准时上班”无需任何常驻调度进程。五、首发保护新联系人先试 5% 再放行买了或爬来的邮箱列表是信誉杀手。MailCheap 的对策在 lib/first-send.js 与配置FIRST_SEND从未收到过邮件的联系人只先发5%最少 200 封、最多 2000 封的一小片其余标记为heldQStash 在 3 小时后投递/api/jobs/check-first-send检查任务统计这一小片的送达、反弹、投诉反弹率超过暂停线2%→ 活动暂停等用户清理列表后手动恢复超过封号线5%→ 整个账号被冻结数据健康 → 放出的held消息继续占剩余时间轴发完。这一小片就像“试菜”出了问题损失被控制在 2000 封以内而不是 20 万封。六、失败与重试签名验证 503 弹性回弹批量任务跑在 Serverless 上任何一环都可能挂。MailCheap 用三层机制兜底签名验证lib/qstash-route.js 的withQStashSignature要求所有任务请求必须由 QStash 签名否则返回 403防止任何人伪造批次任务刷你的发送配额限流回弹服务商返回限流TooManyRequestsException时批次把未发出的消息改回queued接口返回 503——QStash 看到非 2xx 会自动按退避策略重试若服务商明确告知恢复时间retryAfter则用publishBatchRetry以延迟任务的形式“定点重发”连续失败熔断一个批次内连续 5 封失败就暂停整场活动并发告警不再硬碰任务连续失败 3 次后进入 QStash 死信队列并触发管理员告警。每封邮件发送前还有一条“认领”操作queued → sent的原子更新保证即使任务被重复投递同一封邮件也只会出栈一次。七、限流车道多实例共享一条速率20 万封邮件会拆成上千个 Serverless 并发实例如何不让它们集体超速lib/send-rate.js 实现了“双保险车道”进程内间隔每个发送通道lane同步预留发送槽位按配置速率sendRatePerSecond逐封排队跨实例共享通过 Upstash Redis 的全局限流器所有实例共享同一个每秒计数Redis 不可用时自动降级为仅进程内限流超限部分交给服务商限流兜底反正会被重试。这样无论 QStash 同时唤醒多少个批次对外的发送速率始终平滑可控。八、关键文件索引想深入源码按这条路径读发送主流程建消息、切批、发布任务lib/sending.js铺开时长与批次启动时间计算lib/send-spread.jsQStash 任务签名与失败告警lib/qstash-route.js批次执行入口app/api/jobs/send-batch/route.js首发检查任务app/api/jobs/check-first-send/route.js定时发送含 QStash 免费层 7 天延迟的接力机制lib/scheduling.js限流车道lib/send-rate.js全局常量批次 50 封、热身 30 天、暂停/封号阈值lib/config.js总结MailCheap 的批量发送架构没有复杂的消息中间件集群靠的是三个朴素思想的组合切小每批 50 封、铺开按已证明量对数增长计算时间轴用 QStash 延迟投递实现、兜底签名验证、原子认领、503 重试与首发试菜。这套设计让 20 万封邮件可以在几小时内平稳出栈同时把对域名信誉的伤害锁在最小范围内——这正是它敢于按“发出的邮件”而非“订阅者数量”收费的底气所在。赞分享【免费下载链接】mailcheapNewsletters where you pay for the emails you send, not for dormant subscribers, at a fraction of the price of beehiiv and the like. Open source: self-host it, or use mailcheap.ai.项目地址https://gitcode.com/gh_mirrors/mai/mailcheap点击查看免费下载相关推荐Seabird终端功能详解在GNOME桌面中无缝执行Kubernetes命令Seabird终端功能详解在GNOME桌面中无缝执行Kubernetes命令 Seabird作为一款原生Kubernetes桌面客户端为开发者提供了直观的集终极指南如何利用Mailpile邮件合并功能批量发送个性化邮件终极指南如何利用Mailpile邮件合并功能批量发送个性化邮件 Mailpile是一款免费开源的现代邮件客户端以其快速响应和用户友好的加密隐私功能著称。本文后端安全即时通讯PHP邮件发送库SwiftMailer全面入门架构概览与发送第一封邮件PHP邮件发送库SwiftMailer全面入门架构概览与发送第一封邮件 SwiftMailer 是一款功能丰富的 PHP邮件发送库 Comprehensiv后端创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考