littlesheepラムです231 次阅读1,670 字 · 约 4 分钟

不用 25 端口的邮局|Devlog · 30

Behind the Scene: Solar Network Mail

在 Solar Network 查看

Solar Network Mail,你可能不陌生了已经,甚至已经使用上这个 @solarpass.one 结尾的邮箱了。不过你肯定不了解它背后的 ElecPostal,支撑 Solar Network Mail 后面的服务器项目。https://github.com/Solsynth/ElecPostal

发件

稍微了解一些邮件怎么工作的都会知道邮件就是邮件服务器从 :25 把邮件丢到另一个服务器的 :25 端口的 SMTP 服务器上。很简单对吧?但是事实上很简单的东西在合规的影响下也会变得异常复杂,比如说如今大多数云服务器厂商为了保证自家 IP 的纯洁度,基本上会封堵 :25 端口的出站流量,也就意味着你不能通过 SMTP 正常的方式把邮件丢到别的服务器上的 SMTP :25 服务,也就是无法发件了。

部分更加离谱的 VPS 提供商还会封堵 :25 的入站,也就是无法收到邮件。面对这种场景也是更无能为力了,建议还是直接换家云服务器购买。

一般来说这种情况印象不大,因为对于一般开发者需要发送什么邮件验证码通过一些云服务厂商提供的邮件推送服务发送即可,稍稍花一点点钱就好。但是对我 Solar Network Mail 的场景这是灾难性的。不知道是为了合规还是吝啬,绝大部分邮件推送服务都要求你使用一个预先设置的发件邮箱列表。换句话说,你在使用 noreply@yourdomain.com 发送邮件之前要先在它们厂商的控制台上把这个 noreply@yourdomain.com 加入他们的列表,而且通常是有限的,就算你可以自动化这个添加过程每个域名能持有的发件列表也是不能够支撑 Solar Network Mail 的使用场景的。

因此我们引入了 AWS SES,亚马逊云的邮件服务(Simple Email Service)。选择亚马逊云的理由是什么呢?不是因为它的价格便宜,而是 SES 有个其他厂家不给的能力,无固定发件列表,也就是说 AWS SES 可以被当作一个 SMTP Relay 来使用。

SMTP Relay:相当于一个代理,一般情况下是改善网络连接性的方式来加快 SMTP 投递。但是我们这里的场景是通过它来帮助我们发件。

此外 SES 还支持 API 发送,也就是我们无需 25 出站端口即可通过 SES 投递。并且他们官方是支持这种用法的。在简单在工单中说明情况之后他们也是毅然决然地给我开放了 SES 的全体使用权限,而且价格也算便宜,也是非常良心了。

自定义域名

因为使用了 SES,我们利用了 SES 的 Identity API 允许用户添加他们自定义的域名,AWS 对于这种使用也没有设定一个最大的域名上线,让我们可以把自定义域名的权限开放给每一个 Stellar Program 订阅者。

同时老牌邮件推送服务的投递成功率也是一等一的,但是因为没有钱买私有 IP 池,对于一些比较机车的 Spam Filter 可能会因为 IP 是 AWS 的而被丢进垃圾邮件中。不过一般来讲这种一刀切的垃圾过滤系统比较少了(除了巨硬),一般内容的权重比较高。

收件

邮件出了发件还要收件,不然这就是个大喇叭,而不是一个邮局。好在 Solar Network 使用的腾讯云没有这么吝啬把 25 端口的入站也封堵了,让我们 ElecPostal 可以直接接受 SMTP 的投递信件。

一般来说这里需要一个 L4 的负载均衡,但是因为我们 ElecPostal 是高贵的 Go 写的,所以性能一等一的好,而且现在的用户体量也暂时不需要这种东西。所以收件就简单的解说了。

附件

邮件还有一个比较大头,或者说重要的事情,就是附件的实现。在这里我们是顺便扩成了 DysonFS 的能力,允许服务之间利用 gRPC(Solar Network 的服务间通讯协议)来下载或者上传附件。

这不仅帮助我们解决了附件用量统计的问题,也可以让我们复用强大的 DysonFS 文件管理系统,来为用户提供更好的体验。

同时用户在写信发送邮件的时候也可以直接使用 DysonFS 的文件,ElecPostal 会从 DFS 下载到那些附件构造一个 eml 一起投递到对方服务器。如果过大则会直接提供一个 DFS 的链接,相当于自带了苹果的 Maildrop。

存储

一般传统的邮件服务器,比如说 postfix 是把邮件放在实体文件系统上以 .eml 的格式存储。这当然可行,而且邮件信息也完整。但是缺点也很明显,如果需要做全文搜索的实现要不要重新构建一个索引,要不就是要使用全盘扫描,效率低下,NG。

所以 ElecPostal 是通过解析邮件的信息,将其序列化成我们 PostgresQL 能够接受的格式。这也允许我们做更多事情关于你的邮件,比如说全文搜索等。一般比较现代的邮件服务器比如说 Gmail 内部也是这么干的。

关于 IMAP 和 SMTP 的协议支持我们通过读取数据库进行反序列化进行的,重新从数据库中构造 eml 邮件信息返回给客户端。不过这里有面临了另一个问题,上文说过我们的附件是分开存储的,所以为了重新构造 eml 我们需要从 DysonFS 下载附件来构建,虽然我们通过本地缓存热附件来加速了近期邮件的读取速度。但是这很明显还是有点缺陷。这也是为什么使用 Solar Network Webmail 或者 API 获取邮件的速度比 SMTP 快的多的主要原因。

又不是不能用.jpeg


基本上这就是 ElecPostal 的绝大部分技术细节了。希望这能够满足你的好奇心,目前 ElecPostal 对邮件的兼容性已经达到绝大部分了,但是还有部分不足,比如说内部 cid 的实现不太好,而且部分反序列化回 eml 的情况有部分问题,不过这些都在逐渐改善。

Thanks for you reading.