构建一个rust生产应用读书笔记四(实战3)

从这一节开始,我们将继续完善邮件订阅生产级应用,根据作者的选型sqlx作为数据库操作的类库,它有如下优点:

它旨在提供高效、安全且易于使用的数据库交互体验。sqlx 支持多种数据库,包括 PostgreSQL、MySQL 和 SQLite,并且通过异步 I/O 提供了高性能的数据访问能力。(如果实在不理解,就当做JDBC吧)

主要特点

异步支持

sqlx 利用了 Rust 的异步/等待(async/await)特性,允许你编写非阻塞的数据库操作代码,这对于高并发的应用尤其有用。

类型安全

sqlx 提供了编译时的类型检查,确保查询语句中的列名和表名与数据库模式匹配。这可以通过宏(如 query! 和 query_as!)实现。

宏支持

sqlx 提供了一些宏来简化常见的数据库操作,例如 query! 用于执行查询并返回结果,query_as! 用于将查询结果直接映射到 Rust 结构体。

连接池:
sqlx 内置了连接池支持,可以方便地管理和复用数据库连接,提高性能。

事务支持

sqlx 提供了事务管理功能,允许你在多个数据库操作之间保持一致性。

多种数据库支持
sqlx 支持 PostgreSQL、MySQL 和 SQLite,你可以根据项目需求选择合适的数据库。

回顾测试问题

#[tokio::test]
async fn subscribe_returns_a_200_for_valid_form_data() {let app_address = spawn_app();let client = reqwest::Client::new();let body = "name=le%20guin&email=ursula_le_guin%40gmail.com";let response = client.post(&format!("{}/subscriptions", &app_address)).header("Content-Type", "application/x-www-form-urlencoded").body(body).send().await.expect("Failed to execute request .");//此处我们希望返回200,但是很明显/subscriptions路径根本不存在,应该返回404assert_eq!(200, response.status().as_u16());
}

上面的单元测试代码,我们无法仅通过查看 API 响应来判断所需的业务结果是否已经实现。而我们感兴趣的是数据是否已成功存储。具体来说,我们想确认新订阅者的详细信息是,否已被持久化。

为此,我们有两个选择:

  1. 利用公共 API 的另一个端点来检查应用程序的状态;
  2. 在测试用例中直接查询数据库。

在条件允许的情况下,建议优先选择第一种方法:通过公共 API 进行检查。这样,测试代码不会依赖于 API 的内部实现细节(例如底层数据库的技术和架构),因此未来的重构不太可能影响这些测试。

然而,我们的 API 目前没有提供任何公共端点来验证订阅者是否存在。虽然可以考虑添加一个 GET /subscriptions 端点来获取现有订阅者的列表,但这会带来安全性问题:我们不希望在没有任何身份验证的情况下将订阅者的姓名和电子邮件暴露在公网上。虽然我们未来可能会添加这样的端点(毕竟我们不希望每次都需要登录生产数据库来检查订阅者列表),但现在为了测试当前功能而专门开发新功能也并不合适。

因此,我们决定暂时在测试用例中编写一个小查询来直接检查数据库。当有更好的测试策略可用时,我们将移除这一临时解决方案。

数据库设置

原文中作者使用的是postgresql 官方镜像,学习的过程中发现国内docker镜像已经不能使用了,如果同学们发现同样的问题,干脆就在本地按照一个postgresql吧,具体怎么安装就不在赘述了,百度上可以找到。

安装完成之后,新建数据库信息如下

数据库账号和密码: postgres / postgres

数据库schema:newsletter

端口: 5432

安装sqlx-cli

cargo install --version=0.5.7 sqlx-cli --no-default-features --features postgres

构件数据库

rust sqlx提供了强大的数据库管理工具,通过脚本 sqlx migration脚本就可以完成数据的表创建,这个和.net core有些类似,原文中脚本使用了docker,由于我本地没有安装docker因此在原有的基础上修改后使用

#!/usr/bin/env bash
set -x
set -eo pipefail
DB_USER=${POSTGRES_USER:=postgres}
DB_PASSWORD="${POSTGRES_PASSWORD:=postgres}"
DB_NAME="${POSTGRES_DB:=newsletter}"
DB_PORT="${POSTGRES_PORT:=5432}">&2 echo "Postgres is up and running on port ${DB_PORT}!"
export DATABASE_URL=postgres://${DB_USER}:${DB_PASSWORD}@localhost:${DB_PORT}/${DB_NAME}
sqlx database create

了解shell脚本的应该都清楚上面的脚本是啥意思,这里就简单的说明一下:

  1. set -x: 这个命令开启脚本的调试模式,它会将执行的每一条命令及其参数输出到标准错误输出(通常是终端)。这对于调试脚本非常有用,因为它可以帮助您了解脚本运行时实际执行了哪些操作。
  2. set -e: 这个命令告诉shell在任何命令返回非零状态(即失败)时立即退出脚本。这可以防止一个错误导致后续命令执行失败或产生意外的结果,从而提高脚本的健壮性。
  3. set -o pipefail: 在使用管道连接多个命令时,pipefail 选项确保整个管道的退出状态为管道中最后一个非零退出状态的命令的状态,而不是默认情况下只考虑管道最后一个命令的退出状态。这对于确保脚本能够正确响应管道中任何一个命令的失败非常重要。

接着定义数据库的用户名,密码登,然后重点是sqlx database create, 根据定义的文件创建了一个名称为newsletter的schema

数据库

接着上面的脚本我们新建一个表,执行命令

#!/usr/bin/env bash
set -x
set -eo pipefail
DB_USER=${POSTGRES_USER:=postgres}
DB_PASSWORD="${POSTGRES_PASSWORD:=postgres}"
DB_NAME="${POSTGRES_DB:=newsletter}"
DB_PORT="${POSTGRES_PORT:=5432}">&2 echo "Postgres is up and running on port ${DB_PORT}!"
export DATABASE_URL=postgres://${DB_USER}:${DB_PASSWORD}@localhost:${DB_PORT}/${DB_NAME}
# sqlx database create
sqlx migrate add create_subscriptions_table

如果不出意外在工程下会创建一个这样的目录和文件 {timestamp}_create_subscriptions_table.sql

执行完建表命令后返回的结果

在新建的sql文件中加入建表语句

-- Add migration script here
CREATE TABLE subscriptions(id uuid NOT NULL,PRIMARY KEY (id),email TEXT NOT NULL UNIQUE, name TEXT NOT NULL,subscribed_at timestamptz NOT NULL
);

字段说明

  • id: 使用 uuid 类型来唯一标识每个订阅者。NOT NULL 约束表示此字段不能为空。
  • email: 存储订阅者的电子邮件地址。TEXT 类型用于存储文本数据,NOT NULL 表示此字段不能为空,UNIQUE 约束确保每个电子邮件地址都是唯一的。
  • name: 存储订阅者的名字。TEXT 类型用于存储文本数据,NOT NULL 表示此字段不能为空。
  • subscribed_at: 存储用户订阅的时间。timestamptz 类型表示带时区的时间戳,NOT NULL 表示此字段不能为空。

原文中此处使用的都是docker完成整个表构件过程,我在网上找了一些资料,找到了一种适合中国程序员的构件方法, 可以忽略上面的步骤

  1. 安装 cargo install sqlx-cli
  2. 新增配置文件touch .env
  3. 在.env文件中新建
DATABASE_URL=postgres://postgres:postgres@127.0.0.1:5432/newsletter

4. 执行

# 创建数据库
sqlx database create
# 创建表脚本
sqlx migrate add create_subscription_table
# 在创建的{timestamp}_create_subscription_table.sql 中增加
-- Add migration script here
CREATE TABLE subscriptions(id uuid NOT NULL,PRIMARY KEY (id),email TEXT NOT NULL UNIQUE, name TEXT NOT NULL,subscribed_at timestamptz NOT NULL
);
执行sql
sqlx migrate run

最终数据库里面实现的结果如下,此处如果是第一次操作,可能会有些莫名奇妙的问题,删除后重新来就行了:

开始第一个查询

Cargo.toml 是 Rust 项目中的一个文件,用于定义项目的元数据以及依赖关系,使用表格样式的 TOML 语法可以让配置更清晰易读。下面是使用表格样式重新格式化的 sqlx 配置

[dependencies.sqlx]
version = "0.5.7"
default-features = false
features = ["runtime-actix-rustls","macros","postgres","uuid","chrono","migrate",
]

添加这些依赖的说明:

runtime-actix-rustls:

  • 用途: 指示 sqlx 使用 Actix 运行时来处理其异步操作,并使用 rustls 作为 TLS 后端。
  • 场景: 如果你的应用基于 Actix Web 框架,并且需要安全的数据库连接(如通过 HTTPS),这个特征是非常有用的。

macros:

  • 用途: 提供对 sqlx::query! 和 sqlx::query_as! 宏的访问。
  • 场景: 这些宏可以简化 SQL 查询的编写和执行,特别是在需要从查询结果中直接映射到 Rust 结构体时。你将在项目中广泛使用这些宏。

postgres:

  • 用途: 解锁 PostgreSQL 特定的功能,包括非标准的 SQL 类型。
  • 场景: 如果你使用 PostgreSQL 作为数据库,这个特征是必需的,因为它提供了对 PostgreSQL 特有类型的全面支持。

uuid:

  • 用途: 添加对 SQL UUID 类型的支持,将其映射到 uuid crate 中的 Uuid 类型。
  • 场景: 如果你的表中有 UUID 类型的列(例如 id 列),这个特征可以帮助你无缝地在 SQL 和 Rust 之间进行类型转换。

chrono:

  • 用途: 添加对 SQL timestamptz 类型的支持,将其映射到 chrono crate 中的 DateTime<T> 类型。
  • 场景: 如果你的表中有时间戳类型的列(例如 subscribed_at 列),这个特征可以确保时间数据在 SQL 和 Rust 之间的正确转换。

migrate:

  • 用途: 提供对 sqlx-cli 工具内部使用的相同函数的访问,用于管理数据库迁移。
  • 场景: 这个特征对于测试和开发阶段非常有用,可以帮助你自动化数据库模式的管理和更新。

配置管理

配置管理在应用程序开发中非常重要,尤其是在需要连接数据库等外部服务时。使用配置文件可以让你更灵活地管理不同的环境(如开发、测试和生产),而不需要硬编码敏感信息或在代码中频繁修改。

代码重新整理

touch src/configuration.rs                
mkdir src/routes
touch src/routes/mod.rs
touch src/routes/health_check.rs
touch src/routes/subscriptions.rs
touch startup.rs  

src 目录展示

总结

这一节内容重点在于数据库的配置,由于内容比较多,把写代码的心得放在下一章,感谢点赞、收藏、关注,你们的支持是我最大的动力

注:各位亲爱的小伙伴们,今年是我从事软件行业的第20年,通过博客记录的方式将我知道的、理解的、有帮助的都分享给大家。同时,也提供就业指导,专业简历优化服务。你们的支持是我最大的动力

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/64394.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

视频直播点播平台EasyDSS推拉流技术结合无人机推流在道路交通巡检场景中的应用

随着城市化进程的加速&#xff0c;交通网络日益复杂&#xff0c;交通巡检工作面临着前所未有的挑战。传统的巡检方式往往依赖于人工巡查或地面监控设备&#xff0c;但这些方法存在巡检范围有限、效率低下等缺点。 无人机凭借其高空视野、灵活机动、实时监控等优势&#xff0c;…

VBA技术资料MF238:ADO提取多文件区域指定数据到工作表

我给VBA的定义&#xff1a;VBA是个人小型自动化处理的有效工具。利用好了&#xff0c;可以大大提高自己的工作效率&#xff0c;而且可以提高数据的准确度。“VBA语言専攻”提供的教程一共九套&#xff0c;分为初级、中级、高级三大部分&#xff0c;教程是对VBA的系统讲解&#…

Mapper代理开发

引入 Mybatis入门方式中&#xff0c;以下代码仍存在硬编码问题 Mapper 代理开发&#xff1a; 目的&#xff1a; 解决原生方式中的硬编码 简化后期执行sql ------下图中&#xff0c;第一段代码是原生硬编码代码块&#xff0c;第二个是引入了Mapper代理开发的代码块。 Mapper代…

ubuntu22.04编译安装Opencv4.8.0+Opencv-contrib4.8.0教程

本章教程,主要记录在Ubuntu22.04版本系统上编译安装安装Opencv4.8.0+Opencv-contrib4.8.0的具体过程。 一、下载opencv和opencv-contrib包 wget https://github.com/opencv/opencv/archive/refs/tags/4.8.0.zip wget https://github.com/opencv/opencv_contrib/archive/refs/…

使用C语言连接MySQL

库的准备 要使用C语言连接mysql&#xff0c;需要使用mysql官网提供的connect库&#xff0c;可以去官网下载&#xff0c;由于我们要下载到 Linux 操作系统中&#xff0c;也可以使用如下指令进行安装库 sudo apt-get install libmysqlclient-dev MySQL连接C/C的库通常会安装在/us…

【Android学习】RxJava

文章目录 资料连接1. Merge & Zip操作符: 合并数据源2. Map & FlapMap & ConcatMap & Buffer: 变换操作符3. retry & retryUntil & retryWhen : 错误处理操作符4. Transformer & Compose 转换操作符5. 网络请求嵌套回调 FlatMap6. 网络请求出错重连…

Mac配置 Node镜像源的时候报错解决办法

在Mac电脑中配置国内镜像源的时候报错,提示权限问题,无法写入配置文件。本文提供解决方法,青测有效。 一、原因分析 遇到的错误是由于 .npm 目录下的文件被 root 用户所拥有,导致当前用户无法写入相关配置文件。 二、解决办法 在终端输入以下命令,输入管理员密码即可。 su…

PyTorch基础入门

目录 前言一、[张量的广播&基本运算](https://www.bilibili.com/video/BV1Gg411u7Lr/?spm_id_from333.999.0.0)1. 张量的广播特性2. 逐点&规约&比较运算 二、张量的线性代数运算1. BLAS & LAPACK2. 矩阵形变及特殊矩阵构造3. 矩阵基本运算4. 矩阵的线性代数运…

【VSCode】常用插件汇总

1 Path Autocomplete&#xff08;路径提示的插件&#xff09; 步骤一&#xff1a;在vscode的扩展搜索中直接搜索Path Autocomplete&#xff0c;直接安装 步骤二&#xff1a;配置 配置 VS Code settings.json "path-autocomplete.pathMappings": {"": &q…

IOS通过WDA自动化中遇到的问题

IOS自动化遇到的问题 搭建WDA环境中遇到的问题1、XCode unsupport iphone xxx.2、创建Bundle Identifier出现问题&#xff1a;Communication with Apple failed3、创建Bundle Identifier出现问题&#xff1a;Automatic signing failed \Signing certificate is invalid4、创建B…

(六)- DRM驱动开发(qcom)

一&#xff0c;Linux Android Display 1&#xff0c;Linux Android Display Software Subsystem 密 2&#xff0c;Linux Android Display Architecture 密 二&#xff0c;DRM/KMS Adreno DPU 1&#xff0c;硬件框图 密 1.1 Qualcomm Adreno DPU 8-Series Overview 密 …

游戏AI实现-寻路算法(GBFS)

贪婪最佳优先算法是宽度优先遍历和贪婪算法结合的产物&#xff0c;使用启发式函数选择当前最低代价的节点&#xff0c;改善了宽度优先遍历需要遍历地图的大量节点来找寻结果的缺点。 算法过程&#xff1a; 1.首先设置开始节点的启发函数值&#xff08;h&#xff09;为0&#…

[Unity]Unity跨平台开发之Android简介

Android要求和兼容 图形接口支持 注意&#xff1a; 新的 Unity 项目默认不支持 OpenGL ES 2.0。 由于硬件和图形 API 的限制&#xff0c;并非所有渲染管道都与 Android 兼容。 图片压缩 Android标准压缩格式是ETC和ASTC。Unity默认压缩格式是ASTC。如果Android设备不支持您选…

监控易在汽车制造行业信息化运维中的应用案例

引言 随着汽车制造行业的数字化转型不断深入&#xff0c;信息化类IT软硬件设备的运行状态监控、故障告警、报表报告以及网络运行状态监控等成为了企业运维管理的关键环节。监控易作为一款全面、高效的信息化运维管理工具&#xff0c;在汽车制造行业中发挥着重要作用。本文将结合…

Trimble天宝三维激光扫描仪在建筑工程竣工测量中的应用【沪敖3D】

竣工测量是建筑项目竣工阶段的一个至关重要的环节&#xff0c;它为建筑工程的质量验收和成果核查提供了核心的参考依据。传统的竣工测量方法&#xff0c;如全站仪测量&#xff0c;主要依赖于现场人工操作&#xff0c;存在一些明显的局限性&#xff0c;例如作业时间长、工作量大…

健康养生:拥抱生活的艺术

健康养生&#xff1a;拥抱生活的艺术 在快节奏的现代生活中&#xff0c;健康已成为我们最宝贵的财富。健康养生&#xff0c;不仅仅是一种生活方式的选择&#xff0c;更是一种对待生活的态度&#xff0c;它关乎于如何在日常中寻找到平衡&#xff0c;让身心得以滋养&#xff0c;…

【C语言程序设计——入门】基本数据类型与表达式(头歌实践教学平台习题)【合集】

目录&#x1f60b; <第1关&#xff1a;print 函数操作> 任务描述 相关知识 编程要求 测试说明 我的通关代码: 测试结果&#xff1a; <第2关&#xff1a;转义字符使用> 任务描述 相关知识 编程要求 测试说明 我的通关代码: 测试结果&#xff1a; <…

C++算法第九天

本篇文章我们继续学习c算法 目录 第一题 题目链接 题目展示 代码原理 暴力解法 二分解法 代码编写 第二题 题目链接 题目展示 代码原理 代码编写 重点回顾 朴素二分 非朴素二分 重点一 重点二 重点三 第一题 题目链接 153. 寻找旋转排序数组中的最小值 - 力…

Mysql学习笔记之SQL-2

上篇文章介绍了SQL语句的第一部分数据定义语言&#xff08;DDL)&#xff0c;这篇文章我们介绍SQL语句的第二部分&#xff0c;数据库操作语言&#xff08;DML&#xff09; 1.简介 DML全称&#xff08;Data Manipulation Language&#xff09;&#xff0c;用来对数据表中的数据…

opencv-python的简单练习

题目1.读取一张彩色图像并将其转换为灰度图。 import cv2 # 读取图片文件 img cv2.imread(./1.png)# 将原图灰度化 img_gray cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)# 输出图片 cv2.imshow(img,img) cv2.imshow(img_g,img_gray) # 进行阻塞 cv2.waitKey(0) 题目2&#xff1a;…