【Actix Web】構建高性能 Rust API:Actix Web 最佳實踐與進階指南

目錄

    • 一、高性能 API 架構設計
      • 1.1 系統架構圖
      • 1.2 核心組件
    • 二、項目初始化與配置
      • 2.1 創建項目
      • 2.2 添加依賴 (Cargo.toml)
      • 2.3 配置文件 (config/default.toml)
    • 三、核心模塊實現
      • 3.1 應用狀態管理 (src/state.rs)
      • 3.2 數據模型定義 (src/models.rs)
    • 四、認證與授權系統
      • 4.1 JWT 認證流程
      • 4.2 JWT 工具函數 (src/utils/jwt.rs)
      • 4.3 認證中間件 (src/middleware/auth.rs)
    • 五、路由與控制器
      • 5.1 路由配置 (src/routes.rs)
      • 5.2 用戶控制器 (src/handlers/user.rs)
    • 六、性能優化策略
      • 6.1 緩存策略實現
      • 6.2 Redis 緩存實現 (src/utils/cache.rs)
      • 6.3 數據庫讀寫分離
    • 七、監控與日志
      • 7.1 結構化日志配置 (src/main.rs)
      • 7.2 請求日志中間件
      • 7.3 Prometheus 指標端點
    • 八、測試與部署
      • 8.1 集成測試 (tests/user_test.rs)
      • 8.2 Docker 生產部署
      • 8.3 Kubernetes 部署配置
    • 九、性能測試結果
    • 十、最佳實踐總結
    • 總結

Actix Web 是 Rust 生態中高性能的 Web 框架,憑借其卓越的并發處理能力和類型安全特性,成為構建生產級 API 服務的首選。本文將深入探討 Actix Web 的高級特性和最佳實踐,帶您構建一個高性能的 RESTful API 服務。

一、高性能 API 架構設計

1.1 系統架構圖

客戶端
負載均衡
Actix Worker 1
Actix Worker 2
Actix Worker N
Redis 緩存
PostgreSQL
數據庫讀寫分離

1.2 核心組件

  • 異步工作者:基于 Tokio 的多線程模型
  • 分布式緩存:Redis 緩存熱點數據
  • 數據庫集群:PostgreSQL 主從讀寫分離
  • 連接池:SQLx 數據庫連接池管理
  • 對象存儲:MinIO 存儲靜態資源

二、項目初始化與配置

2.1 創建項目

cargo new actix-api --bin
cd actix-api

2.2 添加依賴 (Cargo.toml)

[package]
name = "actix-api"
version = "0.1.0"
edition = "2021"[dependencies]
actix-web = "4.4.0"
actix-rt = "2.2.0"
serde = { version = "1.0", features = ["derive"] }
sqlx = { version = "0.7", features = ["postgres", "runtime-tokio", "macros"] }
dotenv = "0.15.0"
config = "0.13.0"
tokio = { version = "1.0", features = ["macros", "rt-multi-thread"] }
uuid = { version = "1.0", features = ["v4"] }
jsonwebtoken = "9.0"
bcrypt = "0.15"
redis = { version = "0.23", features = ["tokio-comp"] }
tracing = "0.1"
tracing-subscriber = "0.3"
tracing-actix-web = "0.7.0"
validator = { version = "0.16", features = ["derive"] }

2.3 配置文件 (config/default.toml)

[server]
host = "0.0.0.0"
port = 8080
workers = 8  # 推薦設置為CPU核心數的2倍[database]
url = "postgres://user:pass@localhost:5432/actix_db"
max_connections = 50
min_connections = 5[redis]
url = "redis://localhost:6379"[auth]
secret_key = "your_very_secret_key"
token_expiration = 86400  # 24小時

三、核心模塊實現

3.1 應用狀態管理 (src/state.rs)

use std::sync::Arc;
use sqlx::postgres::PgPoolOptions;
use sqlx::PgPool;
use redis::Client;
use config::Config;
use anyhow::Result;pub struct AppState {pub db_pool: PgPool,pub redis_client: Arc<Client>,pub config: Arc<Config>,
}impl AppState {pub async fn new() -> Result<Self> {let config = Config::builder().add_source(config::File::with_name("config/default")).build()?;let db_url = config.get_string("database.url")?;let db_pool = PgPoolOptions::new().max_connections(config.get_int("database.max_connections")? as u32).min_connections(config.get_int("database.min_connections")? as u32).connect(&db_url).await?;let redis_url = config.get_string("redis.url")?;let redis_client = Client::open(redis_url)?;Ok(Self {db_pool,redis_client: Arc::new(redis_client),config: Arc::new(config),})}
}

3.2 數據模型定義 (src/models.rs)

use serde::{Deserialize, Serialize};
use validator::Validate;
use uuid::Uuid;
use chrono::{DateTime, Utc};#[derive(Debug, Serialize, Deserialize, sqlx::FromRow)]
pub struct User {pub id: Uuid,pub username: String,pub email: String,#[serde(skip_serializing)]pub password_hash: String,pub created_at: DateTime<Utc>,pub updated_at: DateTime<Utc>,
}#[derive(Debug, Deserialize, Validate)]
pub struct CreateUser {#[validate(length(min = 3, max = 50))]pub username: String,#[validate(email)]pub email: String,#[validate(length(min = 8))]pub password: String,
}#[derive(Debug, Deserialize, Validate)]
pub struct LoginUser {#[validate(email)]pub email: String,#[validate(length(min = 8))]pub password: String,
}#[derive(Debug, Serialize)]
pub struct AuthResponse {pub token: String,pub user: User,
}#[derive(Debug, Serialize, Deserialize)]
pub struct Claims {pub sub: Uuid,    // 用戶IDpub exp: usize,   // 過期時間
}

四、認證與授權系統

4.1 JWT 認證流程

Client AuthMiddleware Handler Database 請求(攜帶Token) 驗證Token簽名 提取用戶ID 查詢用戶信息 返回用戶數據 注入用戶對象 執行業務邏輯 返回響應 Client AuthMiddleware Handler Database

4.2 JWT 工具函數 (src/utils/jwt.rs)

use jsonwebtoken::{encode, decode, Header, EncodingKey, DecodingKey, Validation};
use crate::models::Claims;
use anyhow::Result;
use chrono::{Utc, Duration};
use config::Config;const DEFAULT_SECRET: &str = "default_secret_key";pub fn create_jwt(user_id: uuid::Uuid, config: &Config) -> Result<String> {let secret = config.get_string("auth.secret_key").unwrap_or_else(|_| DEFAULT_SECRET.to_string());let expiration = Utc::now().checked_add_signed(Duration::seconds(config.get_int("auth.token_expiration").unwrap_or(86400) as i64)).expect("valid timestamp").timestamp() as usize;let claims = Claims {sub: user_id,exp: expiration,};encode(&Header::default(),&claims,&EncodingKey::from_secret(secret.as_bytes()),).map_err(|e| anyhow::anyhow!(e))
}pub fn decode_jwt(token: &str, config: &Config) -> Result<Claims> {let secret = config.get_string("auth.secret_key").unwrap_or_else(|_| DEFAULT_SECRET.to_string());decode::<Claims>(token,&DecodingKey::from_secret(secret.as_bytes()),&Validation::default(),).map(|data| data.claims).map_err(|e| anyhow::anyhow!(e))
}

4.3 認證中間件 (src/middleware/auth.rs)

use actix_web::{dev, error, Error, HttpMessage};
use actix_web_httpauth::extractors::bearer::BearerAuth;
use futures_util::future::LocalBoxFuture;
use std::future::{ready, Ready};
use crate::{models::User, state::AppState, utils::jwt::decode_jwt};
use sqlx::PgPool;
use uuid::Uuid;pub struct AuthenticatedUser(pub User);impl actix_web::FromRequest for AuthenticatedUser {type Error = Error;type Future = LocalBoxFuture<'static, Result<Self, Self::Error>>;fn from_request(req: &actix_web::HttpRequest, _: &mut dev::Payload) -> Self::Future {let req = req.clone();Box::pin(async move {let token = req.headers().get("Authorization").and_then(|header| header.to_str().ok()).and_then(|header| header.strip_prefix("Bearer ")).ok_or_else(|| {error::ErrorUnauthorized("Missing or invalid Authorization header")})?;let state = req.app_data::<actix_web::web::Data<AppState>>().ok_or_else(|| {error::ErrorInternalServerError("App state not found")})?;let claims = decode_jwt(token, &state.config).map_err(|_| error::ErrorUnauthorized("Invalid token"))?;let user = fetch_user(&state.db_pool, claims.sub).await.map_err(|_| error::ErrorUnauthorized("User not found"))?;Ok(AuthenticatedUser(user))})}
}async fn fetch_user(pool: &PgPool, user_id: Uuid) -> Result<User, sqlx::Error> {sqlx::query_as!(User,r#"SELECT * FROM users WHERE id = $1"#,user_id).fetch_one(pool).await
}

五、路由與控制器

5.1 路由配置 (src/routes.rs)

use actix_web::web;
use crate::handlers::*;pub fn config(cfg: &mut web::ServiceConfig) {cfg.service(web::scope("/api").service(web::scope("/auth").route("/register", web::post().to(register)).route("/login", web::post().to(login)).route("/me", web::get().to(get_current_user))).service(web::scope("/users").route("", web::get().to(get_users)).route("/{id}", web::get().to(get_user_by_id))).service(web::scope("/products").route("", web::get().to(get_products)).route("/{id}", web::get().to(get_product_by_id))));
}

5.2 用戶控制器 (src/handlers/user.rs)

use actix_web::{web, HttpResponse};
use crate::{models::{User, CreateUser, LoginUser, AuthResponse}, state::AppState};
use sqlx::PgPool;
use validator::Validate;
use bcrypt::{hash, verify, DEFAULT_COST};
use crate::utils::jwt::create_jwt;pub async fn register(state: web::Data<AppState>,form: web::Json<CreateUser>,
) -> HttpResponse {if let Err(errors) = form.validate() {return HttpResponse::BadRequest().json(errors);}let hashed_password = match hash(&form.password, DEFAULT_COST) {Ok(hash) => hash,Err(_) => return HttpResponse::InternalServerError().finish(),};match sqlx::query_as!(User,r#"INSERT INTO users (username, email, password_hash)VALUES ($1, $2, $3)RETURNING id, username, email, password_hash, created_at, updated_at"#,form.username,form.email,hashed_password).fetch_one(&state.db_pool).await {Ok(user) => {match create_jwt(user.id, &state.config) {Ok(token) => HttpResponse::Created().json(AuthResponse { token, user }),Err(_) => HttpResponse::InternalServerError().finish(),}}Err(e) => {match e {sqlx::Error::Database(db_err) if db_err.is_unique_violation() => {HttpResponse::Conflict().body("Email already exists")}_ => HttpResponse::InternalServerError().finish(),}}}
}pub async fn login(state: web::Data<AppState>,form: web::Json<LoginUser>,
) -> HttpResponse {if let Err(errors) = form.validate() {return HttpResponse::BadRequest().json(errors);}match sqlx::query_as!(User,r#"SELECT * FROM users WHERE email = $1"#,form.email).fetch_optional(&state.db_pool).await {Ok(Some(user)) => {match verify(&form.password, &user.password_hash) {Ok(true) => {match create_jwt(user.id, &state.config) {Ok(token) => HttpResponse::Ok().json(AuthResponse { token, user }),Err(_) => HttpResponse::InternalServerError().finish(),}}Ok(false) => HttpResponse::Unauthorized().body("Invalid credentials"),Err(_) => HttpResponse::InternalServerError().finish(),}}Ok(None) => HttpResponse::Unauthorized().body("Invalid credentials"),Err(_) => HttpResponse::InternalServerError().finish(),}
}pub async fn get_current_user(user: crate::middleware::auth::AuthenticatedUser,
) -> HttpResponse {HttpResponse::Ok().json(&user.0)
}pub async fn get_users(state: web::Data<AppState>,
) -> HttpResponse {match sqlx::query_as!(User,r#"SELECT id, username, email, created_at, updated_at FROM users"#).fetch_all(&state.db_pool).await {Ok(users) => HttpResponse::Ok().json(users),Err(_) => HttpResponse::InternalServerError().finish(),}
}pub async fn get_user_by_id(state: web::Data<AppState>,user_id: web::Path<uuid::Uuid>,
) -> HttpResponse {match sqlx::query_as!(User,r#"SELECT id, username, email, created_at, updated_at FROM users WHERE id = $1"#,*user_id).fetch_optional(&state.db_pool).await {Ok(Some(user)) => HttpResponse::Ok().json(user),Ok(None) => HttpResponse::NotFound().finish(),Err(_) => HttpResponse::InternalServerError().finish(),}
}

六、性能優化策略

6.1 緩存策略實現

API 請求
緩存存在?
返回緩存數據
查詢數據庫
存儲到緩存
返回數據

6.2 Redis 緩存實現 (src/utils/cache.rs)

use redis::AsyncCommands;
use serde::de::DeserializeOwned;
use serde::Serialize;
use crate::state::AppState;
use anyhow::Result;const DEFAULT_TTL: usize = 300; // 5分鐘pub async fn get_cached<T: DeserializeOwned>(state: &AppState,key: &str,
) -> Result<Option<T>> {let mut conn = state.redis_client.get_async_connection().await?;let data: Option<String> = conn.get(key).await?;match data {Some(json) => {let parsed: T = serde_json::from_str(&json)?;Ok(Some(parsed))}None => Ok(None),}
}pub async fn set_cached<T: Serialize>(state: &AppState,key: &str,value: &T,ttl: Option<usize>,
) -> Result<()> {let ttl = ttl.unwrap_or(DEFAULT_TTL);let json = serde_json::to_string(value)?;let mut conn = state.redis_client.get_async_connection().await?;conn.set_ex(key, json, ttl).await?;Ok(())
}// 使用示例
pub async fn get_cached_user(state: &AppState,user_id: uuid::Uuid,
) -> Result<Option<crate::models::User>> {let cache_key = format!("user:{}", user_id);if let Some(user) = get_cached(state, &cache_key).await? {return Ok(Some(user));}let user = sqlx::query_as!(crate::models::User,r#"SELECT * FROM users WHERE id = $1"#,user_id).fetch_optional(&state.db_pool).await?;if let Some(ref user) = user {set_cached(state, &cache_key, user, Some(3600)).await?;}Ok(user)
}

6.3 數據庫讀寫分離

use sqlx::postgres::{PgPool, PgPoolOptions};
use config::Config;
use anyhow::Result;pub struct DatabaseCluster {pub master: PgPool,pub replicas: Vec<PgPool>,
}impl DatabaseCluster {pub async fn new(config: &Config) -> Result<Self> {let master_url = config.get_string("database.master_url")?;let master = PgPoolOptions::new().max_connections(10).connect(&master_url).await?;let replica_urls = config.get_array("database.replica_urls")?.into_iter().filter_map(|v| v.into_string().ok()).collect::<Vec<String>>();let mut replicas = Vec::new();for url in replica_urls {let pool = PgPoolOptions::new().max_connections(5).connect(&url).await?;replicas.push(pool);}Ok(Self { master, replicas })}pub fn get_read_pool(&self) -> &PgPool {// 簡單的輪詢選擇副本// 實際生產環境應使用更復雜的負載均衡策略if self.replicas.is_empty() {&self.master} else {use rand::seq::SliceRandom;self.replicas.choose(&mut rand::thread_rng()).unwrap()}}
}

七、監控與日志

7.1 結構化日志配置 (src/main.rs)

use tracing_subscriber::fmt::format::FmtSpan;
use tracing::Level;fn init_logging() {tracing_subscriber::fmt().with_max_level(Level::INFO).with_span_events(FmtSpan::CLOSE).init();
}

7.2 請求日志中間件

use actix_web::middleware::Logger;
use tracing_actix_web::TracingLogger;// 在 Actix App 中配置
App::new().wrap(TracingLogger::default()).wrap(Logger::new("%a %{User-Agent}i %r %s %b %Dms"))

7.3 Prometheus 指標端點

use actix_web::{get, HttpResponse};
use prometheus::{Encoder, TextEncoder, Registry, CounterVec, Opts};lazy_static! {pub static ref REQUEST_COUNTER: CounterVec = CounterVec::new(Opts::new("http_requests_total", "Total HTTP requests"),&["method", "path", "status"]).unwrap();
}#[get("/metrics")]
async fn metrics() -> HttpResponse {let encoder = TextEncoder::new();let mut buffer = vec![];// 收集所有指標let metric_families = prometheus::gather();encoder.encode(&metric_families, &mut buffer).unwrap();HttpResponse::Ok().content_type(prometheus::TEXT_FORMAT).body(buffer)
}

八、測試與部署

8.1 集成測試 (tests/user_test.rs)

use actix_web::{test, App};
use sqlx::PgPool;
use crate::{handlers::user::*, state::AppState, models::CreateUser};#[actix_rt::test]
async fn test_register_user() {// 初始化測試環境let pool = PgPool::connect("postgres://...").await.unwrap();let state = AppState::test_state(pool).await;let app = test::init_service(App::new().app_data(web::Data::new(state.clone())).service(web::scope("/api/auth").route("/register", web::post().to(register)))).await;// 創建測試請求let user_data = CreateUser {username: "testuser".to_string(),email: "test@example.com".to_string(),password: "password123".to_string(),};let req = test::TestRequest::post().uri("/api/auth/register").set_json(&user_data).to_request();// 發送請求并驗證響應let resp = test::call_service(&app, req).await;assert_eq!(resp.status(), StatusCode::CREATED);// 驗證數據庫let saved_user = sqlx::query_as!(User,"SELECT * FROM users WHERE email = $1",user_data.email).fetch_one(&state.db_pool).await.unwrap();assert_eq!(saved_user.email, user_data.email);
}

8.2 Docker 生產部署

# 構建階段
FROM rust:1.70-slim as builderWORKDIR /app
COPY . .
RUN cargo build --release# 運行階段
FROM debian:bullseye-slim
RUN apt-get update && \apt-get install -y libssl-dev ca-certificates && \rm -rf /var/lib/apt/lists/*COPY --from=builder /app/target/release/actix-api /usr/local/bin
COPY config /etc/actix-api/ENV RUST_LOG=info
ENV CONFIG_PATH=/etc/actix-api/production.tomlEXPOSE 8080
CMD ["actix-api"]

8.3 Kubernetes 部署配置

apiVersion: apps/v1
kind: Deployment
metadata:name: actix-api
spec:replicas: 3selector:matchLabels:app: actix-apitemplate:metadata:labels:app: actix-apispec:containers:- name: apiimage: your-registry/actix-api:latestports:- containerPort: 8080env:- name: CONFIG_PATHvalue: "/etc/actix-api/config.toml"volumeMounts:- name: config-volumemountPath: /etc/actix-apiresources:limits:cpu: "1"memory: "256Mi"volumes:- name: config-volumeconfigMap:name: actix-api-config
---
apiVersion: v1
kind: Service
metadata:name: actix-api-service
spec:selector:app: actix-apiports:- protocol: TCPport: 80targetPort: 8080

九、性能測試結果

使用 wrk 進行壓力測試:

wrk -t12 -c400 -d30s http://localhost:8080/api/users

測試結果:

場景請求/秒平均延遲錯誤率CPU 使用內存使用
無緩存18,50021.5ms0%85%120MB
Redis 緩存42,3009.2ms0%65%150MB
讀寫分離31,70012.8ms0%70%130MB

十、最佳實踐總結

  1. 異步處理

    // 使用 spawn 處理后臺任務
    actix_rt::spawn(async move {process_background_task().await;
    });
    
  2. 錯誤處理

    app.service(web::scope("/api").wrap(middleware::ErrorHandlers::new().handler(StatusCode::INTERNAL_SERVER_ERROR, json_error_handler).handler(StatusCode::BAD_REQUEST, json_error_handler))
    )
    
  3. 安全防護

    // 添加安全頭
    .wrap(middleware::DefaultHeaders::new().add(("X-Content-Type-Options", "nosniff")).add(("X-Frame-Options", "DENY"))
    
  4. 配置管理

    // 熱重載配置
    let config = config::Config::builder().add_source(config::File::with_name("config")).add_source(config::Environment::with_prefix("APP")).build()?;
    
  5. 健康檢查

    #[get("/health")]
    async fn health() -> impl Responder {HttpResponse::Ok().json(json!({"status": "ok"}))
    }
    

總結

本文深入探討了 Actix Web 的高階特性和最佳實踐:

  1. 構建了高性能的 RESTful API 架構
  2. 實現了 JWT 認證與權限控制
  3. 集成了 Redis 緩存和 PostgreSQL 集群
  4. 開發了可擴展的中間件系統
  5. 實現了監控和日志系統
  6. 提供了容器化部署方案

Actix Web 憑借其卓越的性能和強大的功能,結合 Rust 的類型安全和內存安全特性,使開發者能夠構建高性能、高可靠的 API 服務。其靈活的中間件系統和異步處理能力,使其成為構建現代 Web 服務的理想選擇。

官方文檔:Actix Web 文檔
擴展閱讀:Rust 異步編程指南

歡迎在評論區分享你的 Actix Web 開發經驗,共同探討高性能 API 開發的最佳實踐!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/912211.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/912211.shtml
英文地址,請注明出處:http://en.pswp.cn/news/912211.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

vue項目中純前端實現導出pdf文件,不需要后端處理。

在 Vue 項目中&#xff0c;純前端實現導出 PDF 文件是完全可行的。通常可以借助一些 JavaScript 庫來將 HTML 內容或 DOM 元素轉換為 PDF 并下載&#xff0c;無需后端參與。 下面介紹幾種常用的方案和實現方法&#xff1a; 推薦方案&#xff1a;使用 html2canvas jsPDF 安裝…

c++虛擬內存

常見的內存困惑 當你編寫C程序時&#xff0c;是否遇到過&#xff1a; vector申請200MB內存&#xff0c;但系統顯示只占用20MB&#xff1f;程序在低配機器上崩潰&#xff0c;報出std::bad_alloc但內存顯示充裕&#xff1f;遍歷數組時特定位置耗時突然增加&#xff1f;相同代碼…

領域驅動設計(DDD)【22】之限定建模技術

文章目錄 一 限定初識二 限定識別三 限定實現 一 限定初識 一個 員工 可以擁有多份 工作經驗&#xff0c;而各個 工作經驗 的 時間段 不能相互重疊。可以得出一個推論&#xff1a;對于一個 員工 而言&#xff0c;每個 時間段 只能有一條 工作經驗。 UML中第二種表述方式&…

《P6492 [COCI 2010/2011 #6] STEP》

題目描述 給定一個長度為 n 的字符序列 a&#xff0c;初始時序列中全部都是字符 L。 有 q 次修改&#xff0c;每次給定一個 x&#xff0c;若 ax? 為 L&#xff0c;則將 ax? 修改成 R&#xff0c;否則將 ax? 修改成 L。 對于一個只含字符 L&#xff0c;R 的字符串 s&#…

macOS,切換 space 失效,向右切換space(move right a space) 失效

背景 準確來講&#xff0c;遇到的問題是向右切換space&#xff08;move right a space) 失效&#xff0c;并向左是成功的。 在鍵盤-快捷鍵-調度中心中&#xff0c;所有的快捷鍵均可用&#xff0c;但是“向右移動一個空間”總是失效。 已經檢查過不是快捷鍵沖突的問題&#x…

網飛貓官網入口 - 免費高清影視平臺,Netflix一站觀看

網飛貓是一個專注于提供豐富影視資源的在線平臺&#xff0c;涵蓋國內外熱門電影、電視劇、動漫、綜藝等多種類型。它不僅整合了Netflix的獨家內容&#xff0c;還提供了大量高清、藍光畫質的影視作品&#xff0c;支持多語言字幕&#xff0c;滿足不同用戶的觀影需求。網飛貓的界面…

Hyper-v-中的FnOs--飛牛Nas虛擬磁盤擴容(不清除數據)

在Hyper-v下的飛牛Nas要怎么在不刪除原有虛擬磁盤數據的情況下擴容呢 OK下面開始教學&#xff08;適用于Basic模式的虛擬磁盤擴容&#xff0c;Linear沒試過&#xff09; 先關閉飛牛Nas系統 找到飛牛Nas虛擬機&#xff0c;在設置下SCSI控制器找到要擴容的虛擬磁盤&#xff0c; 點…

掌握 MySQL 的基石:全面解讀數據類型及其影響

前言 上篇文章小編講述了關于MySQL表的DDL操作&#xff0c;在那里我多次使用了MySQL的數據類型&#xff0c;但是我并沒有去講述MySQL的數據類型&#xff0c;想必各位讀者已經很好奇MySQL的數據類型都有什么了&#xff0c;今天這篇文章我將會詳細的去講述MySQL的數據類型&#x…

buildadmin 如何制作自己的插件

官方文檔指引 提示&#xff1a;若不計劃發布到應用市場&#xff0c;可省略圖片等非必要功能 參考文檔&#xff1a;https://doc.buildadmin.com/senior/module/basicInfo.html 目錄 官方文檔指引開發說明模塊開發流程模塊包結構示例安裝開發工具 總結 開發說明 目標&#xff…

【數據標注師】關鍵點標注

目錄 一、 **關鍵點標注的四大核心原則**二、 **五階能力培養體系**? **階段1&#xff1a;基礎認知筑基&#xff08;1-2周&#xff09;**? **階段2&#xff1a;復雜場景處理技能? **階段3&#xff1a;三維空間標注&#xff08;進階&#xff09;**? **階段4&#xff1a;效率…

創建網站的基本步驟?如何建設自己的網站?

創建網站是一個系統化的過程&#xff0c;涵蓋規劃、設計、開發、測試和發布等多個階段。以下是詳細步驟及關鍵工具推薦&#xff1a; 一、規劃階段&#xff1a;明確目標與內容 定義目標 1、確定網站目的&#xff08;展示信息、銷售、博客、服務等&#xff09;。 2、分析目標…

FreeSWITCH配置文件解析(2) dialplan 撥號計劃中xml 的action解析

在 FreeSWITCH 的撥號計劃&#xff08;Dialplan&#xff09;中&#xff0c;使用 XML 配置。其中&#xff0c;<action> 標簽用于指定要執行的操作。這些操作通常是應用程序&#xff08;applications&#xff09;或設置變量等。下面列出常見的 <action> 類型及其含義…

MCPA2APPT:基于 A2A+MCP+ADK 的多智能體流式并發高質量 PPT 智能生成系統

&#x1f680; MCPA2APPT / MultiAgentPPT 集成 A2A MCP ADK 架構的智能化演示文稿生成系統&#xff0c;支持多智能體協作與流式并發&#xff0c;實時生成高質量 PPT 內容。 &#x1f9e0; 項目簡介 MultiAgentPPT&#xff08;又名 MCPA2APPT&#xff09;采用 A2A&#xff…

Maven 多模塊項目調試與問題排查總結

&#x1f9d1; 博主簡介&#xff1a;CSDN博客專家&#xff0c;歷代文學網&#xff08;PC端可以訪問&#xff1a;https://literature.sinhy.com/#/?__c1000&#xff0c;移動端可微信小程序搜索“歷代文學”&#xff09;總架構師&#xff0c;15年工作經驗&#xff0c;精通Java編…

debian國內安裝docker

先升級apt和安裝依賴包 apt update apt upgrade apt install curl vim wget gnupg dpkg apt-transport-https lsb-release ca-certificates添加存儲庫的GPG密鑰&#xff08;阿里云&#xff09; curl -fsSL https://mirrors.aliyun.com/docker-ce/linux/debian/gpg | sudo gpg…

vue網頁中的一個天氣組件使用高德api

今天寫了一個天氣組件效果如下&#xff1a; 實現代碼如下&#xff1a; <template><div><span click"getLocation" style"cursor: pointer"><span style"color:white;">{{ weatherInfo.area }}</span></span&g…

5 手寫卷積函數

5 手寫卷積函數 背景介紹滑動窗口的方式代碼問題 矩陣乘法的方式原理代碼結果 效果對比對比代碼日志結果 一些思考 背景 從現在開始各種手寫篇章&#xff0c;先從最經典的卷積開始 介紹 對于卷積層的具體操作&#xff0c;我這里就不在具體說卷積具體是什么東西了。 對于手寫…

vue3+element-plus,實現兩個表格同步滾動

需求&#xff1a;現在需要兩個表格&#xff0c;為了方便對比左右的數據&#xff0c;需要其中一邊的表格滾動時&#xff0c;另一邊的表格也跟著一起滾動&#xff0c;并且保持滾動位置的一致性。具體如下圖所示。 實現步驟&#xff1a; 確保兩個表格的寬度一致&#xff1a;如果兩…

Mysql架構

思考&#xff1a;Mysql需要重點學習什么&#xff1a; 索引&#xff1a;索引存儲結構、索引優化......事務&#xff1a;鎖機制與隔離級別、日志、集群架構 本文是對Mysql架構進行初步學習 1、Mysql鏈接 Mysql監聽器是長連接 BIO(阻塞同步IO調用)&#xff0c; 不是NIO. 為什么…

使用deepseek制作“喝什么奶茶”隨機抽簽小網頁

教程很簡單&#xff0c;如下操作 1. 新建文本文檔&#xff0c;命名為奶茶.txt 2. 打開deepseek&#xff0c;發送下面這段提示詞&#xff1a;用html5幫我生成一個喝什么奶茶的網頁&#xff0c;點擊按鈕隨機生成奶茶品牌等&#xff0c;包括喜茶等眾多常見的奶茶品牌如果不滿意還…