工程实践 · 2026 年 9 月 21 日
事务性邮件迁移实战手册
在不丢失可观测性的前提下迁移事务性邮件服务商,需要分阶段推进:双发、事件对等映射,以及逐步的 DNS 切换。
迁移的核心挑战
要在迁移事务性邮件时不丢失可观测性,您必须将发送触发与服务商实现解耦。具体策略是实现一个服务商抽象层,支持双发(影子发送)和事件映射。先将一小部分流量路由到新服务商,同时继续通过 Webhook 跟踪投递事件,这样您就能在切换主流量之前,确认新服务商能够受理邮件,并且您的可观测性管道能够捕获结果。
为什么要迁移
大多数迁移出于成本、开发者体验或合规方面的考虑。例如,各服务商之间的成本差距相当大。根据 Amazon SES 价格,按量计费的发送成本为每 1,000 封邮件 0.10 USD。相比之下,Postmark 价格起价为每月 15 USD、含 10,000 封邮件,超出部分每 1,000 封 1.80 至 1.20 USD。发送 50,000 封邮件,在 SES 按量计费下约为 5 USD,而在 Postmark 的套餐下约为 66 USD。
其他驱动因素还包括转向仅在欧盟处理、隐私数据最小化的遥测,或需要更好的智能体就绪能力(例如支持 MCP 服务器)。无论原因是什么,风险都是一样的:过渡期间,您的投递管道会出现盲区。
第 1 阶段:抽象层
如果您的应用在业务逻辑中直接调用服务商的 SDK,您就被锁定了。您需要一个统一请求和响应格式的封装层。
统一负载
定义一个与服务商无关的内部结构。这样,您的应用就不必关心底层 API 期望的 to 是数组还是单个字符串。
{
"message_id": "msg_12345",
"recipient": "user@example.com",
"template_id": "welcome_email",
"variables": {
"name": "Alex"
},
"idempotency_key": "unique_request_id_789"
}
在处理 AI 智能体或自动化工作流时,把邮件视为外部副作用至关重要。您必须使用幂等键,确保智能体循环重试时不会把同一封事务性邮件给同一个用户发送五次。
第 2 阶段:DNS 与身份配置
在发送任何一封邮件之前,您必须先建立发信身份。大多数迁移正是在这一步因 DNS 传播延迟或配置错误而失败的。
- 验证域名:添加新服务商的 DKIM 和 SPF 记录。使用 SendHQ 邮件 DNS 检查工具等工具,确认您的记录已生效且格式正确。
- 理解这些记录:请确保您清楚 SPF(授权服务器)与 DKIM(为邮件签名)之间的区别。如果迁移期间同时使用多家服务商,您的 SPF 记录必须同时包含它们。
- DMARC 对齐:在迁移初期,请确保 DMARC 策略设置为
p=none,以免对齐稍有偏差就导致硬退信。详细配置步骤请参阅 SendHQ 的 DKIM、SPF 和 DMARC 指南。
第 3 阶段:影子发送(双发)
不要一刀切地直接切换。而是实现一套路由逻辑:向主服务商发送邮件,同时异步向新服务商发送一份副本(或按比例抽样)。
实现逻辑
async function sendEmail(payload) {
// Primary send (Current Provider)
const primaryResult = await primaryProvider.send(payload);
// Shadow send (New Provider) - do not await or block the main thread
if (Math.random() < 0.1) { // 10% sample
newProvider.send(payload).catch(err =>
console.error("Shadow send failed", err)
);
}
return primaryResult;
}
在这一阶段,您测试的是服务商受理,也就是服务商表示“好的,我接收这封邮件”的那一刻。它不同于投递(邮件到达收件服务器),也不同于收件箱送达(邮件没有进入垃圾邮件文件夹)。
第 4 阶段:可观测性与事件对等
可观测性是指能够追踪一封邮件从 sent 到 delivered 或 bounced 的全过程。每家服务商的 Webhook 结构都不相同。
映射事件
创建一张映射表,将事件统一规范后写入内部数据库:
内部事件 | Amazon SES | Resend | Postmark | SendHQ
sent(已发送) | Send | sent | Sent | sent
delivered(已送达) | Delivery | delivered | Delivered | delivered
bounced(退信) | Bounce | bounced | Bounced | bounced
complaint(投诉) | Complaint | complained | Complaint | complaint
处理 Webhook 负载
您的 Webhook 监听程序应当是通用的。收到来自新服务商的负载时,应先经过转换器处理,再进入您的分析引擎。
function transformWebhook(provider, payload) {
switch(provider) {
case 'resend':
return { event: payload.data.delivered ? 'delivered' : 'failed', id: payload.data.id };
case 'sendhq':
return { event: payload.event, id: payload.message_id };
default:
throw new Error("Unknown provider");
}
}
第 5 阶段:逐步切换
确认新服务商能够受理邮件、Webhook 也能正确映射事件之后,就可以转为按权重分配流量。
- 1% 流量:将所有事务性邮件的 1% 路由到新服务商,并监控退信率。
- 10% 流量:增加负载,检查速率限制。例如,Resend 的免费版每天上限为 100 封邮件,在测试期间可能成为瓶颈。
- 50% 流量:这是稳定性测试。确保延迟保持在可接受范围内。
- 100% 流量:最终切换。
常见迁移故障排查
“静默丢弃”
有些服务商会受理邮件(202 Accepted),却因为内容过滤或发件人身份未验证而在内部将其丢弃。这就是影子发送阶段必不可少的原因。如果您的 sent 事件很多,而 delivered 事件很少,那么您遇到的是投递问题,而不是 API 问题。
速率限制突增
不同服务商的突发上限各不相同。Mailgun 价格和 SendGrid 价格(其免费版现已改为 60 天试用)通常对应不同的吞吐配额。如果您从一个高上限的账户迁移到一个新账户,可能会被限流。请引入队列(如 RabbitMQ 或 SQS)来平滑流量高峰。
幂等失效
切换服务商时,您可能会意外触发某个批次的重试。如果您使用 AI 智能体来触发邮件,请确保智能体提供唯一的请求 ID。如果智能体通过 MCP 服务器与您的邮件 API 交互,API 应在 24 小时窗口内拒绝重复的 idempotency_key 值。
迁移清单
- 已实现抽象层(服务商无关的请求体)。
- 已为新服务商添加 DNS 记录(SPF、DKIM)。
- 已通过 sendhq.cc/tools/email-dns-checker 验证 DNS。
- 已更新 Webhook 监听器,以处理新服务商的架构。
- 已完成事件映射表(已发送、已送达、已退信、投诉)。
- 影子发送以 1% 至 10% 的比例处于活动状态。
- 已验证智能体驱动发送的幂等键。
- 逐步提升(1%、10%、50%、100%)。
- 达到 100% 稳定运行 7 天后撤销旧服务商的 API 密钥。
关于选择服务商的最后思考
选择服务商是在成本与开发速度之间的权衡。如果您需要绝对最低的成本,按量计费每 1,000 封邮件 0.10 USD 的 Amazon SES 很难被超越,不过自 2026 年 7 月 21 日起,其新的分级套餐(Essentials 为 0.16 USD,Pro 为 0.22 USD)引入了不同的成本结构。如果您需要一款内置智能体就绪能力、遥测仅在欧盟处理且隐私数据最小化的现代 API,SendHQ 提供了一个更精简的替代方案。
无论选择哪家服务商,目标都是让您的工程团队不被某个特定服务商的 SDK 所束缚。把邮件视为一种标准化的副作用,就能把高风险的迁移变成一次例行的配置变更。
访问 https://sendhq.cc,了解更多关于构建可靠邮件工作流的内容。