# kiri-mail-server 架构设计文档 ## 一、功能概述 基于 PHP + Swoole 的生产级邮件服务器。 ### Phase 1 (已完成): SMTP 收信 - SMTP 协议接收邮件 (RFC 5321) - MIME 邮件解析 - Maildir 存储 ### Phase 2 (已完成): 邮件队列与外发投递 - Redis 持久化发送队列 - DNS MX 记录查询 - SMTP 客户端外发投递 - 指数退避重试策略 - 死信队列 - 退信生成 - 速率限制 (令牌桶) ## 二、架构组件 ``` ┌──────────────────────────────────────────────────────┐ │ SmtpServer │ │ (Swoole TCP Server :25) │ ├──────────────────────────────────────────────────────┤ │ │ │ ┌─────────────┐ ┌────────────┐ ┌───────────────┐ │ │ │ SmtpSession │ │ MailParser │ │ MaildirStorage │ │ │ │ (收信会话) │ │ (解析邮件) │ │ (本地存储) │ │ │ └──────┬──────┘ └────────────┘ └───────────────┘ │ │ │ │ │ │ 远程域名 → 入队 │ │ ▼ │ │ ┌─────────────────┐ │ │ │ MailQueue │ │ │ │ (Redis 持久化队列) │ │ │ └────────┬────────┘ │ │ │ │ └───────────┼────────────────────────────────────────────┘ │ ┌───────────▼────────────────────────────────────────────┐ │ OutboundDelivery │ │ (外发投递进程) │ ├─────────────────────────────────────────────────────────┤ │ │ │ ┌──────────┐ ┌──────────┐ ┌───────────────┐ │ │ │DnsResolver│ │SmtpClient │ │ RateLimiter │ │ │ │(MX 查询) │ │(远程投递) │ │ (速率控制) │ │ │ └──────────┘ └──────────┘ └───────────────┘ │ │ │ │ 重试策略: 60s → 300s → 900s → 1800s → 3600s → 死信 │ │ │ └─────────────────────────────────────────────────────────┘ ``` ## 三、邮件投递流程 ``` ┌──────────┐ │ SMTP 客户端│ │ (MUA/MSA) │ └─────┬─────┘ │ 连接 :25/:587 ▼ ┌──────────────────┐ │ SmtpServer │ │ (接收邮件) │ └────────┬─────────┘ │ ┌─────────▼─────────┐ │ 收件人域名判断 │ └────┬──────────┬───┘ │ │ 本地域名 │ │ 远程域名 ▼ ▼ ┌──────────┐ ┌──────────┐ │ Maildir │ │MailQueue │ │ 本地存储 │ │Redis 队列 │ └──────────┘ └────┬─────┘ │ ▼ ┌────────────────┐ │OutboundDelivery │ │ 外发投递进程 │ └───────┬────────┘ │ ┌─────────────▼─────────────┐ │ 投递结果 │ └──┬──────────┬──────────┬──┘ │ │ │ 成功 │ 临时失败│ 永久失败│ ▼ ▼ ▼ ┌──────┐ ┌──────┐ ┌──────────┐ │ 移除 │ │ 重试 │ │ 生成退信 │ └──────┘ └──────┘ └──────────┘ ``` ## 四、Redis 数据结构 ``` mail:queue:outbound — ZSET, score=下次尝试时间, member=队列ID mail:queue:outbound:{id} — Hash, 邮件元数据 mail:queue:outbound:dead — ZSET, 死信队列 mail:dns:mx:{domain} — String(JSON), MX 查询缓存 TTL 300s mail:ratelimit:global — String(INT), 全局速率计数器 mail:ratelimit:domain:{d} — String(INT), 域名速率计数器 ``` ## 五、目录结构 ``` kiri-mail-server/ ├── composer.json ├── DESIGN.md ├── README.md ├── config/ │ └── mail.php ├── src/ │ ├── SmtpServer.php # Swoole TCP 服务器 (收信) │ ├── SmtpSession.php # SMTP 会话状态机 │ ├── SmtpProtocol.php # 协议命令解析 │ ├── SmtpResponse.php # 响应构建器 │ ├── SmtpClient.php # SMTP 客户端 (发信) │ ├── SmtpCommand.php # 命令值对象 │ ├── SmtpDeliveryResult.php # 投递结果值对象 │ ├── MailParser.php # MIME 解析 │ ├── MailMessage.php # 邮件消息 │ ├── MailQueue.php # Redis 邮件队列 │ ├── DnsResolver.php # DNS MX 解析 │ ├── RateLimiter.php # 速率限制器 │ ├── OutboundDelivery.php # 外发投递调度 │ ├── OutboundDeliveryProcess.php # 外发投递进程 │ ├── SmtpServerProcess.php # 收信进程 │ ├── MailServerProviders.php # Provider │ ├── Storage/ │ │ ├── StorageInterface.php │ │ └── MaildirStorage.php │ └── Auth/ │ ├── AuthInterface.php │ └── SimpleAuth.php └── tests/ ``` ## 六、进程部署 ```php // config/servers.php 'process' => [ \Kiri\MailServer\SmtpServerProcess::class, // 收信 :25 \Kiri\MailServer\OutboundDeliveryProcess::class, // 外发投递 ], ```