从零构建Rust生产级服务
豆瓣
【美】Luca Palmieri 温祖彤 / 李力 …
简介
《从零构建Rust生产级服务》是一本面向Rust后端开发人员的入门参考书,通过实际项目引导读者从0到1构建一个功能齐全的电子邮件通信API。本书涵盖了广泛的主题,包括Rust生态系统的利用、应用结构的设计、测试的编写、用户认证和授权、错误处理策略的实施、应用状态的观察,以及持续集成和部署管道的建立等。本书不仅介绍了具体的工具和库,还深入探讨了系统设计、可观测性和易操作性等重要概念,能够帮助读者掌握专业的开发方法。
《从零构建Rust生产级服务》适合初学者,是开启Rust开发之旅的理想起点,即使没有Rust或后端开发经验,相信你也能够轻松跟上、快速入门。
目录
第1章 准备工作 1
1.1 安装Rust工具链 1
1.1.1 编译目标 1
1.1.2 发布渠道 2
1.1.3 我们需要什么样的工具链 2
1.2 项目初始化 3
1.3 集成开发环境 3
1.3.1 rust-analyzer 4
1.3.2 IntelliJ Rust 4
1.3.3 应该如何选择IDE 4
1.4 内部开发循环 5
1.4.1 更快的链接 5
1.4.2 cargo-watch 6
1.5 持续集成 7
1.5.1 持续集成的步骤 8
1.5.2 准备就绪的持续集成流水线 10
第2章 构建邮件简报 12
2.1 引导示例 12
2.1.1 基于问题的学习 12
2.1.2 帮助完善本书 13
2.2 邮件简报服务应该做什么 13
2.2.1 捕捉需求:用户故事 13
2.3 循序渐进,不断迭代 14
2.3.1 准备开始 15
第3章 注册新的订阅者 16
3.1 前期准备工作 16
3.2 选择一个Web框架 17
3.3 实现第一个端点:健康检查 17
3.3.1 使用actix-web编写代码 18
3.3.2 actix-web应用程序剖析 19
3.3.3 实现健康检查处理器 24
3.4 第一次集成测试 27
3.4.1 如何对端点进行测试 27
3.4.2 应该将测试放在哪里 28
3.4.3 改变项目结构以便于测试 30
3.5 实现第一个集成测试 33
3.5.1 优化 36
3.6 重新聚焦 40
3.7 处理HTML表单 40
3.7.1 提炼需求 40
3.7.2 以测试的形式捕捉需求 41
3.7.3 从POST请求中解析表单数据 44
3.8 存储数据:数据库 52
3.8.1 选择数据库 52
3.8.2 选择数据库包 53
3.8.3 带有副作用的集成测试 55
3.8.4 数据库初始化 56
3.8.5 编写第一个查询 62
3.9 持久化一个新的订阅者 70
3.9.1 actix-web中的应用程序状态 70
3.9.2 actix-web工作进程 72
3.9.3 Data提取器 74
3.9.4 INSERT语句 74
3.10 更新测试 78
3.10.1 测试隔离 81
3.11 总结 85
第4章 遥测 86
4.1 未知的未知 86
4.2 可观测性 87
4.3 日志 88
4.3.1 log包 89
4.3.2 actix-web的Logger中间件 89
4.3.3 外观模式 90
4.4 插桩POST /subscriptions 92
4.4.1 与外部系统的交互 93
4.4.2 像用户一样思考 94
4.4.3 日志应当易于关联 96
4.5 结构化日志 98
4.5.1 tracing包 99
4.5.2 从log迁移到tracing 99
4.5.3 tracing中的跨度 100
4.5.4 插桩future 102
4.5.5 tracing的Subscriber 104
4.5.6 tracing-subscriber 105
4.5.7 tracing-bunyan-formatter 105
4.5.8 tracing-log 107
4.5.9 删除未使用的依赖 109
4.5.10 清理初始化流程 109
4.5.11 集成测试中的日志 112
4.5.12 清理插桩代码——tracing::instrument 116
4.5.13 保护隐私——secrecy 119
4.5.14 请求ID 121
4.5.15 借力tracing生态系统 124
4.6 总结 124
第5章 上线 125
5.1 我们必须讨论部署问题 125
5.2 选择工具 126
5.2.1 虚拟化:Docker 126
5.2.2 托管:DigitalOcean 127
5.3 应用程序的Dockerfile 127
5.3.1 Dockerfile 127
5.3.2 构建上下文 128
5.3.3 sqlx离线模式 129
5.3.4 运行镜像 131
5.3.5 网络 132
5.3.6 层次化配置 133
5.3.7 数据库连接 138
5.3.8 优化Docker镜像 139
5.4 部署到DigitalOcean应用平台 144
5.4.1 安装 144
5.4.2 应用规范 144
5.4.3 如何使用环境变量注入加密信息 147
5.4.4 连接到DigitalOcean的Postgres实例 149
5.4.5 应用配置中的环境变量 152
5.4.6 最后一步,推送 153
第6章 拒绝无效的订阅者(第一部分) 155
6.1 需求 156
6.1.1 姓名约束 156
6.1.2 安全约束 156
6.2 第一次实现 158
6.3 漏洞百出的验证 159
6.4 类型驱动开发 160
6.5 所有权遇见不变量 164
6.5.1 AsRef 167
6.6 panic 169
6.7 Result——将错误作为值 171
6.7.1 使解析函数返回Result类型 171
6.8 精确的断言错误:claim 174
6.9 单元测试 175
6.10 处理 Result 177
6.10.1 match 177
6.10.2 “?”操作符 178
6.10.3 400的请求错误 179
6.11 电子邮件地址格式 179
6.12 SubscriberEmail类型 180
6.12.1 拆分domain子模块 180
6.12.2 新类型的框架 181
6.13 属性测试 184
6.13.1 使用fake生成随机测试数据 184
6.13.2 quickcheck与proptest 185
6.13.3 quickcheck入门 185
6.13.4 实现Arbitrary特质 186
6.14 请求体验证 188
6.14.1 使用TryFrom重构 192
6.15 总结 195
第7章 拒绝无效的订阅者(第二部分) 196
7.1 确认邮件 196
7.1.1 订阅者的同意 196
7.1.2 确认用户的流程 197
7.1.3 实现策略 198
7.2 邮件发送组件——EmailClient 198
7.2.1 如何发送电子邮件 198
7.2.2 如何使用reqwest编写REST客户端 201
7.2.3 如何测试REST客户端 208
7.2.4 EmailClient::send_email的初版实现 213
7.2.5 加强正常的测试 221
7.2.6 处理失败情况 228
7.3 可维护测试套件的骨架和原则 237
7.3.1 为什么要编写测试 238
7.3.2 为什么不编写测试 238
7.3.3 测试代码也是代码 238
7.3.4 测试套件 239
7.3.5 测试发现 240
7.3.6 每个测试文件都是一个包 241
7.3.7 共享测试辅助函数 241
7.3.8 共享启动逻辑 244
7.3.9 构建API客户端 252
7.3.10 小结 256
7.4 重新聚焦 256
7.5 零停机部署 257
7.5.1 可靠性 257
7.5.2 部署策略 258
7.6 数据库迁移 261
7.6.1 状态存在于应用程序之外 261
7.6.2 部署与迁移 261
7.6.3 多步迁移 262
7.6.4 新的必填字段 262
7.6.5 新表 263
7.7 发送确认邮件 264
7.7.1 固定的电子邮件地址 264
7.7.2 固定的确认链接 269
7.7.3 等待确认 272
7.7.4 GET /subscriptions/confirm的骨架 276
7.7.5 整合 278
7.7.6 订阅令牌 287
7.8 数据库事务 294
7.8.1 全部成功,或者全部失败 294
7.8.2 Postgres中的事务 295
7.8.3 sqlx中的事务 295
7.9 总结 299
第8章 错误处理 300
8.1 错误处理的目的 300
8.1.1 系统内部错误 301
8.1.2 系统交互错误 303
8.1.3 小结 305
8.2 为操作人员提供错误报告 305
8.2.1 跟踪错误的根本原因 308
8.2.2 Error特质 313
8.3 控制流与错误处理 316
8.3.1 控制流的分层 316
8.3.2 使用枚举对错误建模 317
8.3.3 只有错误类型还不够 319
8.3.4 使用thiserror减少样板代码 323
8.4 防止“大泥球”型的错误枚举 324
8.4.1 使用anyhow擦除错误类型 329
8.4.2 使用anyhow还是thiserror 331
8.5 错误日志由谁来记 331
8.6 总结 333
第9章 投递邮件简报 334
9.1 用户故事在变化 334
9.2 不要向未确认的订阅者发送 335
9.2.1 使用公共API设置状态 336
9.2.2 scoped mock 337
9.2.3 绿色测试 338
9.3 所有已确认的订阅者都会收到新内容 339
9.3.1 组合测试辅助函数 339
9.4 实现策略 341
9.5 请求体的内容 341
9.5.1 测试无效输入 342
9.6 获取已确认的订阅者列表 344
9.7 发送邮件简报 346
9.7.1 context与with_context 347
9.8 验证存储的数据 348
9.8.1 责任界限 352
9.8.2 关注编译器 353
9.8.3 移除样板代码 355
9.9 简单方法的局限性 356
9.10 总结 357
第10章 API的安全性 358
10.1 认证 358
10.1.1 缺点 359
10.1.2 多因素身份验证 359
10.2 基于密码的身份验证 359
10.2.1 基本身份验证 360
10.2.2 密码验证——简单的方法 365
10.2.3 密码存储 368
10.2.4 不要阻塞异步执行器 384
10.2.5 用户枚举 390
10.3 这安全吗 395
10.3.1 传输层安全性 395
10.3.2 密码重置 395
10.3.3 交互类型 395
10.3.4 机器对机器 396
10.3.5 使用浏览器的用户 396
10.3.6 机器对机器(机器代表人) 397
10.4 插曲:下一步计划 397
10.5 登录表单 398
10.5.1 提供HTML页面 398
10.6 登录 401
10.6.1 HTML表单 401
10.6.2 登录成功后的重定向 404
10.6.3 处理表单数据 405
10.6.4 上下文错误 412
10.7 会话 443
10.7.1 基于会话的身份验证 443
10.7.2 会话存储 444
10.7.3 选择会话存储 444
10.7.4 actix-session 445
10.7.5 管理仪表板 448
10.8 种子用户 459
10.8.1 数据库迁移 459
10.8.2 密码重置 460
10.9 重构 477
10.9.1 如何实现一个actix-web中间件 478
10.10 总结 484
第11章 容错的工作流 486
11.1 重温POST /admin/newsletters 486
11.2 我们的目标 488
11.3 失败模式 488
11.3.1 无效输入 488
11.3.2 网络I/O 489
11.3.3 应用程序崩溃 490
11.3.4 作者的操作 490
11.4 幂等简介 490
11.4.1 幂等实践:支付 491
11.4.2 幂等键 492
11.4.3 并发请求 493
11.5 测试需求(第一部分) 493
11.6 实现策略 495
11.6.1 有状态的幂等:保存和重放 495
11.6.2 无状态的幂等:确定性的键生成 495
11.6.3 时间问题有些棘手 495
11.6.4 做出选择 496
11.7 幂等存储 496
11.7.1 应该使用哪种数据库 496
11.7.2 数据库模式 497
11.8 保存和重放 498
11.8.1 读取幂等键 498
11.8.2 查询已保存的响应 502
11.8.3 保存响应 505
11.9 并发请求 512
11.9.1 测试需求(第二部分) 512
11.9.2 同步 513
11.10 处理错误 520
11.10.1 分布式事务 522
11.10.2 后向恢复 522
11.10.3 前向恢复 523
11.10.4 异步处理 523
11.11 后记 539