Rust异步编程实战:构建高性能网络应用

Rust异步编程实战:构建高性能网络应用

Rust异步编程实战:构建高性能网络应用

在这里插入图片描述

一、异步编程概述

1.1 同步vs异步的区别

💡在传统的同步编程中,代码按照顺序执行,每个操作必须等待前一个完成才能继续。例如,发送网络请求时,主线程会阻塞直到响应返回,这种方式简单直观,但在高并发场景下效率低下,因为大量线程会因阻塞而闲置。

异步编程则允许代码在等待操作完成时继续执行其他任务。当一个异步操作开始后,程序会立即返回并继续处理下一个任务,直到该操作完成后通过回调或事件通知继续执行后续代码。这种方式显著提高了CPU利用率和系统的并发处理能力。

1.2 Rust异步编程的演进

Rust的异步编程经历了几个重要阶段:

  • 早期阶段:依赖futures库提供基础的Future和Executor支持,但语法冗长且难以使用。
  • 2018 Edition:引入了async/await语法糖的实验版本,简化了异步代码的编写。
  • 2021 Editionasync/await正式稳定,成为Rust异步编程的标准范式。
  • 生态成熟:Tokio、async-std等异步运行时库的发展,以及大量异步IO库的出现,使Rust在异步编程领域具备了强大的生产能力。

1.3 核心概念:Future、Poll、Waker

在Rust异步编程中,有三个核心概念:

Future(未来)

Future是异步操作的抽象表示,它代表一个尚未完成但最终会产生结果的计算。在Rust中,Future是一个trait,定义如下:

usestd::future::Future;usestd::pin::Pin;usestd::task::{Context,Poll};pubtraitFuture{typeOutput;fnpoll(self:Pin<&mutSelf>, cx:&mutContext)->Poll<Self::Output>;}

💡Output是异步操作的结果类型。poll方法是Future的核心,它尝试推进异步操作的执行。

Poll(轮询)

Pollpoll方法的返回类型,用于表示异步操作的状态:

  • Poll::Ready(value):异步操作已完成,返回结果value
  • Poll::Pending:异步操作尚未完成,需要再次调用poll方法。
Waker(唤醒器)

当Future返回Pending时,它需要一种方式通知Executor(执行器)在操作完成后再次轮询。Waker就是负责这个通知机制的组件。在Context结构体中可以获取到Waker实例,Future可以在需要时保存Waker,以便操作完成后唤醒Executor。

二、async/await语法基础

2.1 async关键字

async关键字用于将代码块或函数标记为异步。异步函数的返回值是一个实现了Future trait的匿名类型。例如:

asyncfnfetch_data()->String{// 模拟网络请求"Hello, async!".to_string()}fnmain(){let future =fetch_data();// 这里并没有执行异步代码println!("Future created");}

💡调用异步函数只会创建一个Future对象,不会立即执行代码。我们需要一个Executor来驱动Future的执行。

2.2 await关键字

await关键字用于暂停异步代码的执行,直到Future完成。await必须在async函数或async代码块中使用。例如:

asyncfnfetch_data()->String{"Hello, async!".to_string()}asyncfnprocess(){println!("Start processing");let data =fetch_data().await;// 暂停执行,等待fetch_data完成println!("Data received: {}", data);}use tokio;#[tokio::main]asyncfnmain(){process().await;// 驱动process()的执行}

⚠️注意:await关键字会自动处理Future的状态和唤醒机制,让异步代码看起来像同步代码一样。

2.3 async函数的返回类型

异步函数的返回类型是一个实现了Future trait的匿名类型。如果需要显式指定返回类型,可以使用Box<dyn Future<Output = T>>。例如:

usestd::future::Future;fnfetch_data()->implFuture<Output=String>{asyncmove{"Hello, async!".to_string()}}#[tokio::main]asyncfnmain(){let data =fetch_data().await;println!("{}", data);}

2.4 异步代码的执行

Rust的异步代码需要一个Executor来执行。Executor负责管理任务的调度、唤醒和执行。常用的Executor有Tokio和async-std。例如,使用Tokio的#[tokio::main]宏可以轻松启动一个异步执行器:

use tokio;#[tokio::main]asyncfnmain(){println!("Hello from async main");// 异步代码}

三、Tokio异步运行时

3.1 Tokio的架构与核心组件

Tokio是Rust最流行的异步运行时库,提供了高性能的网络IO、定时器、任务调度等功能。它的架构主要由以下组件构成:

  • Scheduler(调度器):负责管理异步任务的执行和调度。
  • Reactor(反应堆):负责处理IO事件,如网络连接、文件操作等。
  • Runtime(运行时):整合Scheduler和Reactor,提供统一的接口供用户使用。

3.2 安装与配置Tokio

在Cargo.toml中添加Tokio依赖:

[dependencies] tokio = { version = "1.0", features = ["full"] } 

💡features = ["full"]会启用Tokio的所有功能。如果需要更小的二进制文件,可以只启用所需的功能,如["rt-multi-thread", "macros"]

3.3 基本使用示例:TCP服务器/客户端

TCP服务器
usetokio::net::TcpListener;usetokio::io::{AsyncReadExt,AsyncWriteExt};#[tokio::main]asyncfnmain(){let listener =TcpListener::bind("127.0.0.1:8080").await.unwrap();println!("Server listening on 127.0.0.1:8080");loop{let(mut socket, addr)= listener.accept().await.unwrap();println!("Accepted connection from {}", addr);tokio::spawn(asyncmove{letmut buf =[0;1024];loop{match socket.read(&mut buf).await{Ok(0)=>{println!("Client {} disconnected", addr);return;}Ok(n)=>{let data =&buf[..n];println!("Received from {}: {}", addr,String::from_utf8_lossy(data)); socket.write_all(data).await.unwrap();}Err(e)=>{println!("Error reading from client {}: {}", addr, e);return;}}}});}}
TCP客户端
usetokio::net::TcpStream;usetokio::io::{AsyncReadExt,AsyncWriteExt};#[tokio::main]asyncfnmain(){letmut stream =TcpStream::connect("127.0.0.1:8080").await.unwrap();println!("Connected to server");let message ="Hello, Tokio!"; stream.write_all(message.as_bytes()).await.unwrap();println!("Sent: {}", message);letmut buf =[0;1024];let n = stream.read(&mut buf).await.unwrap();let response =String::from_utf8_lossy(&buf[..n]);println!("Received: {}", response);}

3.4 Tokio的任务调度与管理

Tokio提供了任务调度功能,可以创建轻量级的异步任务。任务分为两种类型:

  • 根任务:通过#[tokio::main]宏创建的主任务。
  • 子任务:通过tokio::spawn函数创建的任务。

任务调度器会自动管理任务的执行,确保CPU资源的高效利用。例如:

use tokio;usestd::time::Duration;#[tokio::main]asyncfnmain(){// 创建三个子任务let task1 =tokio::spawn(async{tokio::time::sleep(Duration::from_secs(1)).await;println!("Task 1 completed");});let task2 =tokio::spawn(async{tokio::time::sleep(Duration::from_secs(2)).await;println!("Task 2 completed");});let task3 =tokio::spawn(async{tokio::time::sleep(Duration::from_secs(3)).await;println!("Task 3 completed");});// 等待所有任务完成 task1.await.unwrap(); task2.await.unwrap(); task3.await.unwrap();println!("All tasks completed");}

四、异步IO操作

4.1 异步文件IO

Tokio提供了异步文件IO的支持。例如,读取文件内容:

usetokio::fs::File;usetokio::io::AsyncReadExt;#[tokio::main]asyncfnmain(){letmut file =File::open("test.txt").await.unwrap();letmut content =String::new(); file.read_to_string(&mut content).await.unwrap();println!("File content: {}", content);}

4.2 异步网络IO

除了TCP,Tokio还支持UDP、Unix域套接字等网络协议。例如,UDP服务器:

usetokio::net::UdpSocket;#[tokio::main]asyncfnmain(){let socket =UdpSocket::bind("127.0.0.1:8080").await.unwrap();println!("UDP server listening on 127.0.0.1:8080");letmut buf =[0;1024];loop{let(n, addr)= socket.recv_from(&mut buf).await.unwrap();println!("Received from {}: {}", addr,String::from_utf8_lossy(&buf[..n])); socket.send_to(&buf[..n], addr).await.unwrap();}}

4.3 异步Redis操作(使用redis-rs库)

redis-rs库提供了异步Redis操作的支持。在Cargo.toml中添加依赖:

[dependencies] redis = { version = "0.22", features = ["tokio-comp"] } 

示例代码:

useredis::AsyncCommands;#[tokio::main]asyncfnmain(){let client =redis::Client::open("redis://127.0.0.1/").unwrap();letmut conn = client.get_async_connection().await.unwrap();// 设置键值对let _:()= conn.set("key","value").await.unwrap();println!("Set key:value");// 获取键值对let value:String= conn.get("key").await.unwrap();println!("Get key: {}", value);// 删除键值对let _:()= conn.del("key").await.unwrap();println!("Del key");}

4.4 异步HTTP请求(使用reqwest库)

reqwest是Rust常用的HTTP客户端库,支持异步操作。在Cargo.toml中添加依赖:

[dependencies] reqwest = "0.11" tokio = { version = "1.0", features = ["full"] } 

示例代码:

use reqwest;#[tokio::main]asyncfnmain(){let url ="https://httpbin.org/get";let response =reqwest::get(url).await.unwrap();println!("Status: {}", response.status());let body = response.text().await.unwrap();println!("Body: {}", body);}

五、异步编程的高级话题

5.1 Pin与Unpin

在异步编程中,Future可能包含自引用结构体,即结构体的某个字段指向自身的另一个字段。这种情况下,Future的内存地址必须固定,否则当Future被移动时,引用会失效。Pin trait就是用来确保Future不会被移动的。

Unpin trait表示Future可以安全地移动。大多数类型默认实现了Unpin,只有包含自引用的类型需要显式实现Pin。例如:

usestd::future::Future;usestd::pin::Pin;usestd::task::{Context,Poll};structMyFuture{ done:bool, data:Option<String>,}implFutureforMyFuture{typeOutput=String;fnpoll(mutself:Pin<&mutSelf>, cx:&mutContext)->Poll<Self::Output>{ifself.done {Poll::Ready(self.data.take().unwrap())}else{self.done =true;self.data =Some("Data from MyFuture".to_string()); cx.waker().wake_by_ref();// 唤醒自己Poll::Pending}}}#[tokio::main]asyncfnmain(){let future =MyFuture{ done:false, data:None};let result = future.await;println!("{}", result);}

5.2 组合Future:join、select、try_join

Future的组合操作可以让我们并发执行多个异步任务,并对结果进行处理。Tokio提供了多种组合器:

join:等待所有任务完成
usetokio::join;usestd::time::Duration;asyncfntask1()->String{tokio::time::sleep(Duration::from_secs(1)).await;"Task 1 completed".to_string()}asyncfntask2()->String{tokio::time::sleep(Duration::from_secs(2)).await;"Task 2 completed".to_string()}#[tokio::main]asyncfnmain(){let(res1, res2)=join!(task1(),task2());println!("{}", res1);println!("{}", res2);}
select:等待第一个任务完成
usetokio::select;usestd::time::Duration;asyncfntask1()->String{tokio::time::sleep(Duration::from_secs(1)).await;"Task 1 completed".to_string()}asyncfntask2()->String{tokio::time::sleep(Duration::from_secs(2)).await;"Task 2 completed".to_string()}#[tokio::main]asyncfnmain(){select!{ res1 =task1()=>println!("{}", res1), res2 =task2()=>println!("{}", res2),}}
try_join:处理返回Result的任务
usetokio::try_join;usestd::time::Duration;asyncfntask1()->Result<String,String>{tokio::time::sleep(Duration::from_secs(1)).await;Ok("Task 1 completed".to_string())}asyncfntask2()->Result<String,String>{tokio::time::sleep(Duration::from_secs(2)).await;Err("Task 2 failed".to_string())}#[tokio::main]asyncfnmain(){matchtry_join!(task1(),task2()){Ok((res1, res2))=>{println!("{}", res1);println!("{}", res2);}Err(e)=>println!("Error: {}", e),}}

5.3 超时与取消

在异步编程中,我们经常需要处理任务超时和取消的情况。Tokio提供了相应的API:

超时
usetokio::time::timeout;usestd::time::Duration;asyncfnlong_running_task()->String{tokio::time::sleep(Duration::from_secs(3)).await;"Task completed".to_string()}#[tokio::main]asyncfnmain(){matchtimeout(Duration::from_secs(2),long_running_task()).await{Ok(res)=>println!("{}", res),Err(e)=>println!("Timeout: {}", e),}}
取消
usetokio::select;usestd::time::Duration;asyncfntask()->String{loop{println!("Task running...");tokio::time::sleep(Duration::from_secs(1)).await;}}#[tokio::main]asyncfnmain(){letmut interval =tokio::time::interval(Duration::from_secs(2));select!{ res =task()=>println!("{}", res), _ = interval.tick()=>println!("Task cancelled"),}}

5.4 错误处理:Result与async/await的结合

异步函数可以返回Result类型,结合?运算符进行错误处理:

use reqwest;usestd::io;asyncfnfetch_data(url:&str)->Result<String,Box<dynstd::error::Error>>{let response =reqwest::get(url).await?;let body = response.text().await?;Ok(body)}asyncfnwrite_to_file(content:&str, filename:&str)->Result<(),Box<dynstd::error::Error>>{letmut file =tokio::fs::File::create(filename).await?;tokio::io::AsyncWriteExt::write_all(&mut file, content.as_bytes()).await?;Ok(())}#[tokio::main]asyncfnmain()->Result<(),Box<dynstd::error::Error>>{let url ="https://httpbin.org/get";let content =fetch_data(url).await?;write_to_file(&content,"data.txt").await?;println!("Data saved to data.txt");Ok(())}

六、实战项目:高性能HTTP API服务

6.1 项目需求与架构设计

我们将构建一个高性能的HTTP API服务,提供用户的CRUD操作。项目需求如下:

  • 支持用户的创建、读取、更新、删除操作
  • 数据存储在PostgreSQL数据库中
  • API响应格式为JSON
  • 支持跨域请求
  • 高性能的异步处理

项目架构设计:

  • 使用Axum作为HTTP框架(轻量级、高性能)
  • 使用Tokio作为异步运行时
  • 使用SQLx作为数据库访问层(异步、类型安全)
  • 使用CORS中间件处理跨域请求
  • 使用Logger中间件记录请求日志

6.2 依赖配置与项目初始化

创建项目:

cargo new rust-async-api cd rust-async-api 

在Cargo.toml中添加依赖:

[dependencies] axum = "0.5" tokio = { version = "1.0", features = ["full"] } sqlx = { version = "0.6", features = ["postgres", "runtime-tokio-rustls"] } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" tower-http = { version = "0.3", features = ["cors", "trace"] } 

初始化PostgreSQL数据库:

CREATEDATABASE rust_async_api;CREATETABLE users ( id SERIALPRIMARYKEY, name VARCHAR(100)NOTNULL, email VARCHAR(100)NOTNULLUNIQUE, created_at TIMESTAMPDEFAULTCURRENT_TIMESTAMP);

6.3 实现API端点

数据库连接

创建src/db.rs:

usesqlx::PgPool;pubasyncfncreate_pool(database_url:&str)->PgPool{PgPool::connect(database_url).await.unwrap()}
用户数据模型

创建src/models.rs:

useserde::{Deserialize,Serialize};usesqlx::FromRow;#[derive(Debug, FromRow, Serialize, Deserialize)]pubstructUser{pub id:i32,pub name:String,pub email:String,pub created_at:chrono::DateTime<chrono::Utc>,}#[derive(Debug, Deserialize)]pubstructCreateUser{pub name:String,pub email:String,}#[derive(Debug, Deserialize)]pubstructUpdateUser{pub name:Option<String>,pub email:Option<String>,}
API路由处理

创建src/handlers.rs:

useaxum::{extract::{Path,State},http::StatusCode,response::IntoResponse,Json,};usesqlx::PgPool;usecrate::models::{CreateUser,UpdateUser,User};pubasyncfnget_users(State(pool):State<PgPool>)->implIntoResponse{let users =sqlx::query_as!(User,"SELECT * FROM users").fetch_all(&pool).await;match users {Ok(users)=>(StatusCode::OK,Json(users)).into_response(),Err(_)=>(StatusCode::INTERNAL_SERVER_ERROR,"Failed to fetch users").into_response(),}}pubasyncfnget_user(Path(user_id):Path<i32>,State(pool):State<PgPool>,)->implIntoResponse{let user =sqlx::query_as!(User,"SELECT * FROM users WHERE id = $1", user_id).fetch_one(&pool).await;match user {Ok(user)=>(StatusCode::OK,Json(user)).into_response(),Err(sqlx::Error::RowNotFound)=>(StatusCode::NOT_FOUND,"User not found").into_response(),Err(_)=>(StatusCode::INTERNAL_SERVER_ERROR,"Failed to fetch user").into_response(),}}pubasyncfncreate_user(State(pool):State<PgPool>,Json(payload):Json<CreateUser>,)->implIntoResponse{let user =sqlx::query_as!(User,"INSERT INTO users (name, email) VALUES ($1, $2) RETURNING *", payload.name, payload.email ).fetch_one(&pool).await;match user {Ok(user)=>(StatusCode::CREATED,Json(user)).into_response(),Err(sqlx::Error::Database(dbe))if dbe.code().unwrap_or("").eq_ignore_ascii_case("23505")=>{(StatusCode::BAD_REQUEST,"Email already exists").into_response()}Err(_)=>(StatusCode::INTERNAL_SERVER_ERROR,"Failed to create user").into_response(),}}pubasyncfnupdate_user(Path(user_id):Path<i32>,State(pool):State<PgPool>,Json(payload):Json<UpdateUser>,)->implIntoResponse{let user =sqlx::query_as!(User,"SELECT * FROM users WHERE id = $1", user_id).fetch_one(&pool).await;if user.is_err(){return(StatusCode::NOT_FOUND,"User not found").into_response();}let user = user.unwrap();let name = payload.name.unwrap_or(user.name);let email = payload.email.unwrap_or(user.email);let updated_user =sqlx::query_as!(User,"UPDATE users SET name = $1, email = $2 WHERE id = $3 RETURNING *", name, email, user_id ).fetch_one(&pool).await;match updated_user {Ok(user)=>(StatusCode::OK,Json(user)).into_response(),Err(sqlx::Error::Database(dbe))if dbe.code().unwrap_or("").eq_ignore_ascii_case("23505")=>{(StatusCode::BAD_REQUEST,"Email already exists").into_response()}Err(_)=>(StatusCode::INTERNAL_SERVER_ERROR,"Failed to update user").into_response(),}}pubasyncfndelete_user(Path(user_id):Path<i32>,State(pool):State<PgPool>,)->implIntoResponse{let result =sqlx::query!("DELETE FROM users WHERE id = $1", user_id).execute(&pool).await;match result {Ok(result)if result.rows_affected()==0=>(StatusCode::NOT_FOUND,"User not found").into_response(),Ok(_)=>(StatusCode::NO_CONTENT,"").into_response(),Err(_)=>(StatusCode::INTERNAL_SERVER_ERROR,"Failed to delete user").into_response(),}}
应用程序入口

创建src/main.rs:

useaxum::{routing::{get, post, put, delete},Router,};usetower_http::{cors::CorsLayer,trace::TraceLayer};usestd::env;usecrate::db::create_pool;usecrate::handlers::{get_users, get_user, create_user, update_user, delete_user};moddb;modhandlers;modmodels;#[tokio::main]asyncfnmain(){// 初始化日志tracing_subscriber::fmt::init();// 从环境变量获取数据库URLlet database_url =env::var("DATABASE_URL").expect("DATABASE_URL is required");let pool =create_pool(&database_url).await;// 配置CORSlet cors =CorsLayer::permissive();// 创建路由let app =Router::new().route("/users",get(get_users)).route("/users",post(create_user)).route("/users/:id",get(get_user)).route("/users/:id",put(update_user)).route("/users/:id",delete(delete_user)).layer(cors).layer(TraceLayer::new_for_http()).with_state(pool);// 启动服务器let listener =tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();println!("Server running on http://0.0.0.0:3000");axum::serve(listener, app).await.unwrap();}

6.4 性能优化与测试

性能优化
  • 连接池:使用SQLx的PgPool管理数据库连接,避免频繁创建和销毁连接。
  • 异步IO:使用Axum和Tokio的异步特性,提高请求处理的并发能力。
  • 查询优化:使用索引优化数据库查询,例如在email字段上创建唯一索引。
性能测试

使用wrk进行性能测试:

wrk -t12 -c400 -d30s http://localhost:3000/users 

测试结果(示例):

Running 30s test @ http://localhost:3000/users 12 threads and 400 connections Thread Stats Avg Stdev Max +/- Stdev Latency 50.19ms 20.12ms 200.00ms 68.00% Req/Sec 666.67 80.00 800.00 80.00% 239999 requests in 30.01s, 50.00MB read Requests/sec: 7997.00 Transfer/sec: 1.67MB 

6.5 部署与监控

部署

可以使用Docker容器化部署API服务。创建Dockerfile:

FROM rust:1.60 as builder WORKDIR /app COPY Cargo.toml Cargo.lock ./ RUN cargo new --bin rust-async-api WORKDIR /app/rust-async-api COPY src ./src RUN cargo build --release FROM debian:buster-slim RUN apt-get update && apt-get install -y ca-certificates && rm -rf /var/lib/apt/lists/* COPY --from=builder /app/rust-async-api/target/release/rust-async-api /usr/local/bin/ EXPOSE 3000 CMD ["rust-async-api"] 

构建并运行Docker容器:

docker build -t rust-async-api .docker run -p 3000:3000 -e DATABASE_URL=postgresql://user:password@host:5432/rust_async_api rust-async-api 
监控

使用Prometheus和Grafana监控API服务的性能。添加Prometheus依赖:

[dependencies] axum-prometheus = "0.4" prometheus = "0.13" 

在src/main.rs中添加Prometheus监控:

useaxum_prometheus::PrometheusMetrics;let prometheus =PrometheusMetrics::new("api",Some("/metrics"));let app =Router::new()// 其他路由.route("/metrics",get(prometheus.metrics)).layer(prometheus.layer())// 其他中间件.with_state(pool);

七、常见问题与最佳实践

7.1 异步编程中的常见错误

  • 阻塞操作:在异步任务中使用同步IO或长时间运行的计算,会阻塞任务调度器,影响性能。解决方案是使用异步版本的API或在单独的线程中执行阻塞操作。
  • 任务泄漏:创建的任务未被正确取消,导致资源泄漏。解决方案是使用Tokio的cancel机制或监控任务的状态。
  • 死锁:多个任务之间相互等待对方完成,导致死锁。解决方案是避免循环依赖或使用超时机制。

7.2 性能优化技巧

  • 最小化任务创建:避免在循环中频繁创建任务,尽可能复用任务。
  • 合理设置线程数:根据CPU核心数调整Tokio的工作线程数。
  • 使用连接池:对数据库、Redis等资源使用连接池,避免频繁创建和销毁连接。
  • 避免不必要的await:在不需要等待的地方避免使用await,提高代码的执行效率。

7.3 代码风格与可读性

  • 使用async/await语法:优先使用async/await语法糖,避免手写Future实现。
  • 模块化设计:将异步代码分为多个模块,提高代码的可读性和可维护性。
  • 错误处理:使用Result类型统一处理错误,并提供有意义的错误信息。
  • 文档注释:为异步函数和方法添加详细的文档注释,说明其功能和使用方法。

7.4 异步生态推荐

  • 异步运行时:Tokio、async-std
  • HTTP框架:Axum、Actix-web、Rocket
  • 数据库访问:SQLx、Diesel(支持异步)
  • Redis操作:redis-rs
  • HTTP客户端:reqwest
  • 异步文件IO:Tokio fs、async-std fs
  • 监控工具:Prometheus、Grafana、Tracing

八、总结

Rust的异步编程提供了高性能、内存安全的并发处理能力。通过async/await语法,我们可以编写出与同步代码类似的异步代码,同时获得异步编程的优势。Tokio作为成熟的异步运行时库,提供了丰富的API和组件,使开发高性能网络应用变得更加简单。

在实际项目中,我们需要注意异步编程的常见错误,遵循最佳实践,同时利用生态系统中的工具和库来提高开发效率和代码质量。希望本章的内容能够帮助您快速掌握Rust异步编程的核心技术,并在实际项目中应用。

Read more

第十六届极客大挑战 web 复现

跟着大佬的文章复现 第十六届极客大挑战 web题型WP | CN-SEC 中文网 第16届极客大挑战-web - J_0k3r 可能还需要做的,学习sql注入脚本咋写的,phar深入理解 Expression 描述:这个程序员偷懒直接复制粘贴网上的代码连 JWT 密钥都不改..? 直接那jwt用爆破一下,发现是secret 但是没东西,发现显示用户名,可能有ssti,emm 从图中可以看到,该项目使用了 Node.js + Express 框架,而 EJS 是 Express 最常用的模板引擎之一(Express 默认支持 EJS) 第一次接触这个模板。 Ejs简介: EJS是一个javascript模板库,用来从json数据中生成HTML字符串 * 功能:缓存功能,能够缓存好的HTML模板; * <% code %>用来执行javascript代码 基础用法: 标签:

By Ne0inhk

智能抠图Rembg部署指南:从零开始搭建WebUI服务

智能抠图Rembg部署指南:从零开始搭建WebUI服务 1. 引言 1.1 智能万能抠图 - Rembg 在图像处理与内容创作领域,自动去背景是一项高频且关键的需求。无论是电商商品图精修、社交媒体素材制作,还是AI绘画后期处理,精准的抠图能力都能极大提升效率。传统方法依赖人工标注或简单边缘检测,效果粗糙且耗时。随着深度学习的发展,基于显著性目标检测的AI模型成为主流解决方案。 其中,Rembg 作为开源社区中广受好评的图像去背工具,凭借其高精度和通用性脱颖而出。它基于 U²-Net(U-square Net) 架构,专为显著性物体分割设计,能够在无需任何人工标注的情况下,自动识别图像主体并生成带有透明通道的PNG图像。 1.2 项目核心价值 本文将带你从零开始,部署一个集成 WebUI界面 + RESTful API + CPU优化推理引擎 的本地化Rembg服务。该方案具备以下优势: * ✅ 完全离线运行:不依赖ModelScope等平台认证,避免Token失效问题 * ✅ 通用性强:不仅限于人像,支持宠物、汽车、

By Ne0inhk

树莓派5部署冬瓜HAOS:从零到智能家居中枢实战

1. 准备工作:硬件与软件选择 在开始部署冬瓜HAOS之前,选择合适的硬件和软件是确保系统稳定运行的关键。树莓派5作为最新的单板计算机,性能比前代提升显著,尤其适合作为智能家居中枢。我实测下来,树莓派5的多核处理能力和更高的内存带宽(支持8GB LPDDR4X)能够轻松应对Home Assistant的多任务需求,比如同时处理传感器数据、摄像头流媒体和自动化规则。 硬件方面,除了树莓派5主板,你需要准备以下配件: * TF卡:推荐使用SanDisk Extreme PRO系列(64GB以上,U3 A2 V30规格)。这种高速卡能显著提升系统响应速度,因为HAOS会频繁读写日志和数据库。我试过用普通Class 10卡,启动时间长了近一倍,偶尔还会卡顿。 * 电源适配器:树莓派5需要27W USB-C PD电源(官方电源最稳)。我用过第三方电源,偶尔会触发低压警告,导致系统不稳定。 * 散热方案:树莓派5运行时CPU温度可能飙到70°C以上,建议加装散热风扇或金属散热片。我用的是一体化散热外壳,待机温度控制在40°C左右。 * 外设:HDMI显示器、

By Ne0inhk
33岁失业女前端程序员,可以转行干什么啊?

33岁失业女前端程序员,可以转行干什么啊?

33岁失业,既没有20+的精力无限,也还没到40+的稳定沉淀,加上前端行业技术迭代快、年轻化竞争激烈的现状,焦虑感扑面而来太正常了。 但作为一名深耕行业多年的观察者,我想先给各位姐妹吃颗定心丸:33岁的前端经验不是“包袱”,而是“宝藏”。咱们多年积累的逻辑思维、用户感知、跨团队沟通能力,以及对技术实现边界的把控,都是转行的核心优势。与其纠结“年龄大了怎么办”,不如聚焦“我的优势能迁移到哪里”。结合行业趋势和女性从业者的特质,整理了6个高适配、易落地的转行方向,供大家参考。 一、技术相关赛道:发挥积累,平稳过渡 如果对技术还有热情,不想彻底脱离IT圈,这类方向能最大化利用前端基础,转型成本最低,也是最容易快速上手的选择。 1. 测试开发工程师:细节控的“降维打击” 前端开发天天和界面打交道,最清楚用户会怎么操作、哪里容易出bug,这种对用户行为的敏感度,是测试开发的核心竞争力。而且咱们懂代码、懂开发流程,从“找bug”升级为“

By Ne0inhk