构建一个rust生产应用读书笔记7-确认邮件3
设计架构思路
从前面的学习过程中,我们从单一文件测试套件发展到模块化测试套件,并构建了一套强大的辅助工具,这是一个非常重要的进展。个人认为测试代码和应用代码一样,是一个持续进化的过程。随着项目的不断成长,测试也需要不断地调整和扩展,以确保新功能的质量以及现有功能的稳定性。
对于接下来要实现的功能——发送确认邮件,我们可以遵循以下步骤来继续推进:
1. 定义需求
明确需要实现的确认邮件功能的具体要求,例如:
- 在什么情况下触发邮件发送(如用户注册、密码重置等)。
- 邮件的内容格式(包括主题、正文、签名等)。
- 收件人的信息来源(从数据库中获取还是其他方式)。
- 是否有特殊的业务逻辑需要考虑(如个性化内容、多语言支持等)。
2. 设计解决方案
根据需求定义,设计出合理的架构和技术方案。比如选择合适的邮件服务提供商(SMTP服务器或者第三方API),确定如何在应用程序中集成这些服务,以及怎样处理发送失败的情况。
3. 编写单元测试
为新的邮件发送功能编写单元测试。这应该涵盖所有可能的成功路径和失败场景。由于涉及到外部系统(邮件服务),可以使用mock对象或stub来模拟这些依赖,保证测试环境的隔离性和可重复性。
4. 创建集成测试
除了单元测试外,还需要创建集成测试来验证整个流程是否按预期工作。这类测试会真正调用邮件服务,所以要考虑成本和速度的问题。可以通过配置测试环境中的特定设置来控制实际发送的邮件数量,或者仅在CI/CD管道中执行这些测试。
5. 开发功能代码
按照设计方案开始编码实现。确保遵循良好的编程实践,如DRY原则(Don't Repeat Yourself)、SOLID原则等,使代码易于维护和扩展。
6. 实施监控与日志记录
为了跟踪邮件发送的状态并及时发现潜在问题,在关键点添加适当的日志记录是非常必要的。此外,还可以考虑建立一个简单的监控系统,用于检测异常情况并报警。
7. 文档化
不要忘记更新相关文档,描述新增加的功能及其使用方法。这对于未来的维护和支持非常重要。
8. 回顾与优化
完成初步实现后,组织一次代码审查会议,邀请团队成员参与讨论,寻找改进的机会。同时也可以回顾一下现有的测试套件,看看是否有地方可以进一步增强其稳定性和覆盖率。
零停机部署的关键要素
-
负载均衡器:使用负载均衡器来分配流量到不同的应用实例。负载均衡器可以在不同版本的应用程序之间切换流量,而不会影响正在处理的请求。
-
蓝绿部署(Blue-Green Deployment):
- 创建两个完全相同的环境,即蓝色环境(当前生产环境)和绿色环境(新版本部署环境)。
- 当新版本准备就绪后,将流量从蓝色环境切换到绿色环境。
- 如果出现问题,可以迅速回滚到蓝色环境,确保最小化风险。
-
滚动更新(Rolling Update):
- 逐步替换旧版本的应用实例为新版本,而不是一次性全部替换。
- 在每次迭代中只更新一部分实例,并监控其健康状态。
- 如果出现任何问题,可以暂停或回滚更新过程。
-
金丝雀发布(Canary Release):
- 先将新版本部署给一小部分用户(如5%的流量),并密切监控这些用户的体验和系统性能。
- 如果一切正常,则逐渐增加新版本的流量比例,直到完全取代旧版本。
-
数据库迁移:
- 对于涉及数据库模式变更的情况,需要特别小心处理以避免数据丢失或不一致。
- 可以采用分阶段的方式进行迁移,先引入向后兼容的新字段或表,再逐步移除旧结构。
-
自动伸缩:根据实际需求动态调整实例数量,保证有足够的资源来应对流量变化。
-
健康检查与自我修复机制:确保有健全的健康检查机制来检测实例的状态,并且能够自动重启故障实例或者重新分配流量。
-
CI/CD管道:构建持续集成和持续交付(CI/CD)管道,自动化测试、构建和部署过程,减少人为错误的风险。
-
配置管理:使用配置管理系统(如Ansible、Puppet、Chef等)来管理和同步配置文件,确保所有实例的一致性。
实施步骤
-
评估现有架构:首先了解目前使用的云服务提供商、网络设置、容器编排工具(如Kubernetes)、微服务框架等信息。
-
选择合适的部署策略:基于业务需求和技术栈选择最适合的零停机部署方式,如蓝绿部署、滚动更新或金丝雀发布等。
-
设计详细的部署计划:包括每个阶段的任务列表、时间安排、责任人以及应急预案。
-
执行前的准备工作:确保所有必要的基础设施组件已经到位,例如额外的服务器容量、更新后的镜像、修改过的配置文件等。
-
实施部署:按照预定的计划执行部署,同时密切关注系统的响应情况和服务质量指标。
-
验证结果:确认新版本已成功上线并且稳定运行,同时收集反馈用于后续改进。
给表添加一个新的列
sqlx migrate add add_status_to_subscriptions
20241226080004_add_status_to_subscriptions.sql内容
-- Add migration script here
ALTER TABLE subscriptions ADD COLUMN status TEXT NULL;
执行命令 : sqlx migrate run
新的列入库
//!src/routers/subscriptions.rs
pub async fn insert_subscriber(
pool: &PgPool,
new_subscriber: &NewSubscriber,
) -> Result<(), sqlx::Error> {
sqlx::query!(
r#"insert into subscriptions (id,email,name,subscribed_at,status) values($1,$2,$3,$4,'confirmed')"#,
Uuid::new_v4(),
new_subscriber.email.as_ref(),
new_subscriber.name.as_ref(),
Utc::now()
)
.execute(pool)
.await
.map_err(|e| {
tracing::error!("Failed to execute query :{:?}", e);
e
})?;
Ok(())
}
历史数据补全
执行新的SQL
sqlx migrate add make_status_not_null_in_subscriptions
20241226083956_make_status_not_null_in_subscriptions.sql
-- Add migration script here
BEGIN;
-- Backfill `status` for historical entries
UPDATE subscriptions
SET status = 'confirmed'
WHERE status IS NULL;
-- Make `status` mandatory
ALTER TABLE subscriptions ALTER COLUMN status SET NOT NULL;
COMMIT;
执行命令 : sqlx migrate run
当我们处理 subscription_tokens
数据库表时,尤其是在部署新功能(如订阅确认邮件)的背景下,遵循一个结构化的流程来最小化停机时间并确保数据完整性是非常重要的。
可以通过如下步骤处理:
-
创建迁移脚本:首先,生成一个新的迁移脚本,用于在数据库中添加
subscription_tokens
表。这个表可能包括字段如令牌值、用户ID(或订阅ID)、创建时间戳以及该令牌是否已被使用等。 -
独立部署迁移:在这个阶段,运行此迁移以在数据库中添加新的表。此时,你的应用程序应该仍然能正常工作,因为它被设计为忽略或不使用它不认识的表。
-
更新应用程序代码:开发并部署应用程序的新版本,使其开始使用
subscription_tokens
表。这将涉及更新代码库以处理令牌的生成、发送确认邮件和验证用户确认订阅时的令牌。 -
彻底测试:在全面推出该特性之前,确保你已经测试了从令牌生成到电子邮件确认的整个流程,以防止任何问题发生。
-
部署后监控:一旦部署完成,密切监控系统,以便尽早捕捉任何未预见的问题。
让我们执行以下脚本
sqlx migrate add create_subscription_tokens_table
--sql 内容
CREATE TABLE subscription_tokens(
subscription_token TEXT NOT NULL,
subscriber_id uuid NOT NULL REFERENCES subscriptions (id),
PRIMARY KEY (subscription_token)
);
-- 执行
sqlx migrate run
总结
书中的第七章内容比较多,这周调代码好了好长时间,下一节就记录我代码重构的过程吧