注册接口里那句"顺便发个欢迎邮件",可能是后端工程里最坑的一句注释。
它听起来像是个脚注,实际上是一整个子系统。用最直观的方式写,它会用最惨烈的方式教你做人。
![]()
先看最朴素的实现,一个函数从上写到下,干净利落:
创建用户、发邮件、返回成功。看起来完美。
问题就出在中间那行——email.send_welcome()。这是你系统可用性消失的地方。
为什么本地跑得好好的,上线就崩?
在你笔记本上,这段代码永远不会出问题。你的笔记本从来没遇到过速率限制。
但在生产环境,这行代码是一次网络往返,目标是一家你完全无法控制的公司。赶上对方状态不好,你的注册接口就跟着遭殃。
它慢。你的注册速度,从此取决于邮件服务商最差的那次响应。你把最差情况下的响应时间交给了别人的值班团队。
它会失败。邮件服务商返回错误时,你的处理函数直接抛异常,整个请求失败。用户看到"出错了",但账号可能已经建好了——取决于你的事务边界画在哪。
它把你的可用性和别人的绑定在一起。串行请求链上每多一个依赖,失败概率就增加。两个99.9%的服务串起来,整体就是99.8%。你不是在"用"邮件服务商,你是在"继承"它的故障。
问题的本质不是技术,是概念
创建账号和发邮件,是两件完全不同的事,紧急程度天差地别。
用户在等第一个。软件史上,没有人坐在屏幕前等一封欢迎邮件。
但你硬是把它们粘在了一起。
解决办法很简单:不要在请求期间做第二件事。
保存用户,立刻返回响应,然后往某个地方丢一条消息:"有封邮件要发"。
另起一组worker去读这些消息,按邮件服务商允许的速度慢慢发。这个存消息的地方就是队列——SQS、RabbitMQ、Redis加个合适的库,随你选。
响应时间从"看邮件API今天心情如何"降到大约40毫秒。
邮件服务商挂一小时?任务在队列里堆着,恢复后自动排空。注册的用户根本察觉不到。
这才是真正的异步解耦——把"用户等待的事"和"用户不等待的事"彻底分开。
特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.