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

到目前为止我们完成了邮件订阅的第一个迭代,但是我们目前还没有创建一个包含HTML表单的网页来实际测试端到端的流程。不过,我们已经完成了一些黑盒集成测试,这些测试主要关注两个基本场景:

  • 当提交有效的表单数据时(如提供姓名和电子邮件),数据会被正确地保存到数据库中。
  • 如果表单数据不完整(例如,缺少电子邮件、姓名或两者皆无),API会返回400状态码。

尽管如此,我们尚未准备好让软件在生产环境中稳定运行。实际上,我们还处于'盲目'的状态:一方面,应用程序本身还未经过全面的检测;另一方面,我们也没有实施任何遥测数据的收集机制,这意味着我们对应用可能面临的未知风险缺乏了解。

为了应对由未知未知引起的故障或错误,我们可以采取哪些措施?

生产级应用需要考虑的问题

1. 监控和日志记录

  • 实施全面监控:使用监控工具跟踪应用程序的健康和性能,包括系统指标、数据库查询和网络流量。这有助于早期发现异常。
  • 详细日志记录 :确保应用程序记录详细的运行信息,特别是关键路径和错误处理。日志应包括时间戳、用户ID、请求详情和错误消息。

2. 错误处理和弹性设计

  • 优雅降级 :设计应用程序以优雅地处理故障。例如,如果数据库连接丢失,应用程序应能够重试连接或向用户提供有意义的错误消息。
  • 断路器:实现断路器以防止级联故障。如果服务开始失败,断路器可以暂时停止向其发送请求,允许其恢复。
  • 速率限制和节流:实施速率限制以保护应用程序免受突然的流量高峰影响,防止资源过载。

3. 测试和验证

  • 负载测试:定期进行负载测试,模拟高流量场景,识别潜在瓶颈。
  • 模糊测试:使用模糊测试通过发送意外或格式错误的输入来识别漏洞。
  • 混沌工程:通过有意引入故障来练习混沌工程,测试系统的弹性。工具如Chaos Monkey可以帮助实现这一点。

4. 应急响应计划

  • 制定应急响应计划:制定明确简洁的应急响应计划,包括角色和责任、沟通协议和诊断及解决问题的步骤。
  • 事后分析:在事件发生后进行事后分析,了解问题所在及如何防止类似问题再次发生。与团队分享发现,提高集体知识水平。

5. 持续学习和改进

  • 保持信息更新 :及时了解最新安全补丁、最佳实践和您技术栈中的新兴威胁。
  • 定期代码审查:定期进行代码审查,提前发现潜在问题,确保代码质量。
  • 安全审计:定期进行安全审计,识别并修复漏洞。

6. 文档和培训

  • 维护关键系统文档 :维护系统的最新文档,包括架构图、部署流程和故障排除指南
  • 培训和演练:培训团队成员熟悉应急响应计划,并定期进行演练,确保每个人在故障发生时知道自己的职责。

7. 社区和外部资源

  • 利用社区知识 :参与开发者社区和论坛,学习他人的经验和最佳实践。
  • 咨询外部专家:考虑咨询外部专家或使用托管服务,以获得额外的见解和支持。

通过实施这些策略,您可以更好地准备您的团队和系统,以应对未知未知,减少故障和错误的发生概率和影响。

日志

在 Rust 中,最常用的日志库是 loglog 提供了五个宏:tracedebuginfowarnerror。这些宏都做同样的事情------生成一条日志记录,但每个宏使用不同的日志级别,正如其名称所暗示的那样。

  • trace 是最低级别的日志:跟踪级别的日志通常非常冗长,信噪比较低(例如,每当 Web 服务器接收到一个 TCP 数据包时,生成一条跟踪级别的日志记录)。
  • 接下来按严重程度递增顺序依次是 debuginfowarnerror
  • error 级别的日志用于报告可能影响用户的严重故障(例如,处理传入请求失败或数据库查询超时)。

让我们来看一个简单的使用示例:

rust 复制代码
rust深色版本
use log::{trace, debug, info, warn, error};

fn main() {
    // 初始化日志记录器
    env_logger::init();

    // 示例日志记录
    trace!("This is a trace-level log message");
    debug!("This is a debug-level log message");
    info!("This is an info-level log message");
    warn!("This is a warn-level log message");
    error!("This is an error-level log message");
}

在这个示例中,我们使用 env_logger 来初始化日志记录器。env_logger 是一个流行的日志记录后端,可以根据环境变量配置日志级别。默认情况下,env_logger 会打印 info 及以上级别的日志消息。

在main.rs 里面添加日志

Cargo.toml 添加依赖

复制代码
[dependencies]
env_logger = "0.9"

使用"info"作为默认值作为日志级别,并启动日志记录功能

rust 复制代码
//!main.rs
#[tokio::main]
async fn main() -> std::io::Result<()> {    
    env_logger::Builder::from_env(Env::default().default_filter_or("info")).init();
   //...
}

日志数据结果

复制代码
#[2024-11-23T00:30:29Z INFO  actix_server::builder] starting 8 workers
[2024-11-23T00:30:29Z INFO  actix_server::server] Tokio runtime found; starting in existing Tokio runtime
[2024-11-23T00:30:29Z INFO  actix_server::server] starting service: "actix-web-service-127.0.0.1:8000", workers: 8, listening on: 127.0.0.1:8000

给subscription.rs 也添加上日志

rust 复制代码
use core::result::Result::{Err, Ok};

use actix_web::{web, HttpResponse};
use chrono::Utc;
use sqlx::PgPool;
use uuid::Uuid;

#[derive(serde::Deserialize)]
pub struct FormData {
    email: String,
    name: String,
}

pub async fn subscribe(form: web::Form<FormData>, pool: web::Data<PgPool>) -> HttpResponse {
    log::info!("[subscriptions].subscribe() save new details in the database");
    match sqlx::query!(
        r#"insert into subscriptions (id,email,name,subscribed_at) values($1,$2,$3,$4)"#,
        Uuid::new_v4(),
        form.email,
        form.name,
        Utc::now()
    )
    .execute(pool.as_ref())
    .await
    {
        Ok(_) => {
           use core::result::Result::{Err, Ok};

use actix_web::{web, HttpResponse};
use chrono::Utc;
use sqlx::PgPool;
use uuid::Uuid;

#[derive(serde::Deserialize)]
pub struct FormData {
    email: String,
    name: String,
}

pub async fn subscribe(form: web::Form<FormData>, pool: web::Data<PgPool>) -> HttpResponse {
    log::info!("[subscriptions].subscribe() save new details in the database");
    match sqlx::query!(
        r#"insert into subscriptions (id,email,name,subscribed_at) values($1,$2,$3,$4)"#,
        Uuid::new_v4(),
        form.email,
        form.name,
        Utc::now()
    )
    .execute(pool.as_ref())
    .await
    {
        Ok(_) => {
            log::info!("[subscriptions].subscribe() new subscriptions has been saved");
            HttpResponse::Ok().finish()
        }
        Err(e) => {
            print!("Failed to execute query:{}", e);
            HttpResponse::InternalServerError().finish()
        }
    }
}

            HttpResponse::Ok().finish()
        }
        Err(e) => {
            print!("Failed to execute query:{}", e);
            HttpResponse::InternalServerError().finish()
        }
    }
}

总结:

这一节比较简单,主要学习了日志的级别,和项目中如何使用,日志在整个项目的开发周期都起着至关重要的作用,同时也希望在学习和工作当中把重要的信息都记录到日志

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

相关推荐
问道飞鱼36 分钟前
【数据库知识】PGSQL常见命令行与函数
数据库·oracle·pgsql
爱奥尼欧1 小时前
【Linux笔记】网络部分——传输层协议TCP(2)
linux·网络·笔记·tcp/ip
qq_479875431 小时前
TcpConnection
运维·服务器·网络
逻极2 小时前
Rust之旅的起点:为什么选择Rust?
开发语言·后端·rust
华如锦2 小时前
使用SSE进行实时消息推送!替换WebSocket,轻量好用~
java·开发语言·网络·spring boot·后端·websocket·网络协议
kyle~3 小时前
计算机网络---ICMP协议(Internet Control Message Protocol,互联网控制消息协议)
网络·计算机网络·智能路由器
肩上风骋4 小时前
ubuntu系统使用ifconfig查询网络IP的时候,只能查到本地回环地址,无其他网络接口IP
网络·ubuntu·只能查到本地回环地址
脑子不好的小菜鸟5 小时前
深入剖析 Rust `HashMap`:安全哈希 (SipHash) 与高性能冲突处理 (Swiss Table)
安全·rust·哈希算法
yics.8 小时前
网络原理——初识网络
网络·计算机网络·通信基础
xrkhy8 小时前
微服务之ShardingSphere
数据库·微服务·oracle