跳到主要内容 Rust 与 Redis 开发实战:构建高性能会话管理系统 | 极客日志
Rust
Rust 与 Redis 开发实战:构建高性能会话管理系统 使用 Rust 语言和 Redis 数据库构建高性能会话管理系统的实战教程。内容涵盖开发环境搭建、Redis 基本操作(字符串、哈希、列表等)、会话管理系统核心代码实现(SessionManager、AuthManager)以及性能优化策略(连接池、批量操作、管道)。通过具体示例展示了如何利用 redis-rs 库进行异步操作,实现用户登录、会话创建、验证及过期清理等功能,适合希望掌握 Rust 后端开发与 Redis 应用的技术人员参考。
信号故障 发布于 2026/3/30 更新于 2026/4/13 1 浏览
Rust 与 Redis 开发实战:构建高性能会话管理系统
一、引言 Redis 是一款高性能的内存数据库,支持多种数据结构,包括字符串、哈希、列表、集合、有序集合等,具有读写速度快、内存占用少、跨平台等特点,非常适合开发缓存、消息队列、会话管理、实时数据分析等应用。
Rust 语言以其内存安全、高性能和良好的工具链支持,成为开发 Redis 应用的理想选择。Rust 生态系统中提供了多个优秀的 Redis 库,其中 redis-rs 是最成熟、最流行的一个。redis-rs 提供了安全、易用的 API,支持 Redis 的全部功能,包括事务处理、管道操作、发布订阅等。
二、开发环境搭建
2.1 安装 Redis Redis 通常已经预装在大多数操作系统中,你可以通过以下命令检查是否安装:
macOS
brew install redis
brew services start redis
Ubuntu/Debian
sudo apt-get update
sudo apt-get install -y redis-server
sudo systemctl start redis-server
Windows
2.2 创建 Rust 项目
cargo new rust-redis-session
cd rust-redis-session
ls -la
2.3 安装 redis-rs 依赖 在 Cargo.toml 中添加 redis-rs 依赖:
[package]
name = "rust-redis-session"
version = "0.1.0"
edition = "2021"
[dependencies]
redis = { version = "0.25" , features = ["tokio-comp" ] }
tokio = { version = "1.0" , features = ["full" ] }
serde = { version = "1.0" , features = ["derive" ] }
serde_json = "1.0"
uuid = { version = "1.0" , features = ["v4" ] }
chrono = { version = "0.4" , features = ["serde" ] }
thiserror = "1.0"
clap = { version = "4.5" , features = ["derive" ] }
redis-rs :Redis 的 Rust 绑定,支持异步操作。
tokio :Rust 的异步运行时。
serde :数据序列化/反序列化库。
serde_json :JSON 数据序列化/反序列化库。
uuid :UUID 生成库。
chrono :日期和时间处理库。
thiserror :自定义错误类型库。
clap :命令行参数解析库。
三、Redis 基本操作
3.1 连接 Redis
use redis::RedisError;
use std::env;
async fn connect_redis () -> Result <redis::Client, RedisError> {
let redis_url = env::var ("REDIS_URL" ).unwrap_or_else (|_| "redis://127.0.0.1:6379" .to_string ());
let client = redis::Client::open (redis_url)?;
println! ("Successfully connected to Redis" );
Ok (client)
}
#[tokio::main]
async fn main () -> Result <(), RedisError> {
let client = connect_redis ().await ?;
Ok (())
}
3.2 字符串操作
use redis::AsyncCommands;
use redis::RedisError;
async fn string_example (client: &redis::Client) -> Result <(), RedisError> {
let mut con = client.get_async_connection ().await ?;
con.set ("username" , "admin" ).await ?;
println! ("Successfully set username: admin" );
let username : String = con.get ("username" ).await ?;
println! ("Successfully got username: {}" , username);
con.set_ex ("token" , "abc123" , 3600 ).await ?;
println! ("Successfully set token: abc123 (expires in 3600 seconds)" );
let token : Option <String > = con.get ("token" ).await ?;
if let Some (token) = token {
println! ("Successfully got token: {}" , token);
} else {
println! ("Token not found" );
}
Ok (())
}
#[tokio::main]
async fn main () -> Result <(), RedisError> {
let client = connect_redis ().await ?;
string_example (&client).await ?;
Ok (())
}
3.3 哈希操作
use redis::AsyncCommands;
use redis::RedisError;
async fn hash_example (client: &redis::Client) -> Result <(), RedisError> {
let mut con = client.get_async_connection ().await ?;
con.hset ("user:1" , "name" , "admin" ).await ?;
con.hset ("user:1" , "email" , "[email protected] " ).await ?;
con.hset ("user:1" , "password" , "password123" ).await ?;
println! ("Successfully set user:1" );
let name : String = con.hget ("user:1" , "name" ).await ?;
let email : String = con.hget ("user:1" , "email" ).await ?;
let password : String = con.hget ("user:1" , "password" ).await ?;
println! ("Successfully got user:1" );
println! ("Name: {}" , name);
println! ("Email: {}" , email);
println! ("Password: {}" , password);
let user : Vec <(String , String )> = con.hgetall ("user:1" ).await ?;
println! ("Successfully got user:1 fields and values: {:?}" , user);
con.hdel ("user:1" , "password" ).await ?;
println! ("Successfully deleted user:1 password" );
Ok (())
}
#[tokio::main]
async fn main () -> Result <(), RedisError> {
let client = connect_redis ().await ?;
hash_example (&client).await ?;
Ok (())
}
3.4 列表操作
use redis::AsyncCommands;
use redis::RedisError;
async fn list_example (client: &redis::Client) -> Result <(), RedisError> {
let mut con = client.get_async_connection ().await ?;
con.lpush ("tasks" , "Learn Rust" ).await ?;
con.lpush ("tasks" , "Learn Redis" ).await ?;
con.lpush ("tasks" , "Build Project" ).await ?;
println! ("Successfully added tasks to list" );
let length : i64 = con.llen ("tasks" ).await ?;
println! ("Tasks list length: {}" , length);
let tasks : Vec <String > = con.lrange ("tasks" , 0 , -1 ).await ?;
println! ("Tasks list: {:?}" , tasks);
let task : String = con.lpop ("tasks" ).await ?;
println! ("Successfully popped task from left: {}" , task);
let task : String = con.rpop ("tasks" ).await ?;
println! ("Successfully popped task from right: {}" , task);
Ok (())
}
#[tokio::main]
async fn main () -> Result <(), RedisError> {
let client = connect_redis ().await ?;
list_example (&client).await ?;
Ok (())
}
3.5 集合操作
use redis::AsyncCommands;
use redis::RedisError;
async fn set_example (client: &redis::Client) -> Result <(), RedisError> {
let mut con = client.get_async_connection ().await ?;
con.sadd ("tags" , "Rust" ).await ?;
con.sadd ("tags" , "Redis" ).await ?;
con.sadd ("tags" , "Web" ).await ?;
println! ("Successfully added tags to set" );
let tags : Vec <String > = con.smembers ("tags" ).await ?;
println! ("Tags set: {:?}" , tags);
let exists : bool = con.sismember ("tags" , "Rust" ).await ?;
println! ("Tag Rust exists: {}" , exists);
con.srem ("tags" , "Web" ).await ?;
println! ("Successfully removed tag Web from set" );
Ok (())
}
#[tokio::main]
async fn main () -> Result <(), RedisError> {
let client = connect_redis ().await ?;
set_example (&client).await ?;
Ok (())
}
3.6 有序集合操作
use redis::AsyncCommands;
use redis::RedisError;
async fn zset_example (client: &redis::Client) -> Result <(), RedisError> {
let mut con = client.get_async_connection ().await ?;
con.zadd ("scores" , "user1" , 90 ).await ?;
con.zadd ("scores" , "user2" , 85 ).await ?;
con.zadd ("scores" , "user3" , 95 ).await ?;
println! ("Successfully added scores to zset" );
let scores : Vec <(String , i32 )> = con.zrange_withscores ("scores" , 0 , -1 ).await ?;
println! ("Scores zset: {:?}" , scores);
let scores_desc : Vec <(String , i32 )> = con.zrevrange_withscores ("scores" , 0 , -1 ).await ?;
println! ("Scores zset (desc): {:?}" , scores_desc);
let score : i32 = con.zscore ("scores" , "user1" ).await ?;
println! ("User1 score: {}" , score);
Ok (())
}
#[tokio::main]
async fn main () -> Result <(), RedisError> {
let client = connect_redis ().await ?;
zset_example (&client).await ?;
Ok (())
}
四、实战项目:高性能会话管理系统
4.1 项目概述
用户登录 :验证用户名和密码
会话创建 :创建新的会话
会话验证 :验证会话的有效性
会话过期 :自动清理过期的会话
会话统计 :统计会话数量
4.2 数据结构设计 使用 Redis 的字符串、哈希、集合等数据结构存储会话数据:
会话 ID :使用 UUID 生成
会话数据 :存储在哈希中,包括用户 ID、用户名、过期时间等
会话集合 :存储所有有效的会话 ID
用户会话映射 :存储用户 ID 到会话 ID 的映射
4.3 代码实现
4.3.1 定义会话结构体
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use uuid::Uuid;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Session {
pub id: String ,
pub user_id: String ,
pub username: String ,
pub created_at: DateTime<Utc>,
pub expires_at: DateTime<Utc>,
}
impl Session {
pub fn new (user_id: String , username: String , expires_in: i64 ) -> Self {
let id = Uuid::new_v4 ().to_string ();
let created_at = Utc::now ();
let expires_at = created_at + chrono::Duration::seconds (expires_in);
Session { id, user_id, username, created_at, expires_at }
}
pub fn is_expired (&self ) -> bool {
Utc::now () > self .expires_at
}
}
4.3.2 定义会话管理结构体
use redis::AsyncCommands;
use redis::RedisError;
use std::collections::HashMap;
use crate::session::Session;
#[derive(Debug)]
pub struct SessionManager {
client: redis::Client,
prefix: String ,
default_expires_in: i64 ,
}
impl SessionManager {
pub fn new (client: redis::Client, prefix: String , default_expires_in: i64 ) -> Self {
SessionManager { client, prefix, default_expires_in }
}
pub async fn create_session (&self , user_id: String , username: String ) -> Result <Session, RedisError> {
let session = Session::new (user_id, username, self .default_expires_in);
let mut con = self .client.get_async_connection ().await ?;
let session_key = format! ("{}:{}" , self .prefix, session.id);
con.hset (&session_key, "user_id" , &session.user_id).await ?;
con.hset (&session_key, "username" , &session.username).await ?;
con.hset (&session_key, "created_at" , &session.created_at.to_rfc3339 ()).await ?;
con.hset (&session_key, "expires_at" , &session.expires_at.to_rfc3339 ()).await ?;
con.expire (&session_key, self .default_expires_in).await ?;
println! ("Successfully created session: {}" , session.id);
let sessions_key = format! ("{}:sessions" , self .prefix);
con.sadd (&sessions_key, &session.id).await ?;
println! ("Successfully added session to sessions set: {}" , session.id);
let user_sessions_key = format! ("{}:user:{}" , self .prefix, session.user_id);
con.sadd (&user_sessions_key, &session.id).await ?;
println! ("Successfully added session to user sessions: {}" , session.id);
Ok (session)
}
pub async fn get_session (&self , session_id: &str ) -> Result <Option <Session>, RedisError> {
let mut con = self .client.get_async_connection ().await ?;
let session_key = format! ("{}:{}" , self .prefix, session_id);
let session_data : Option <HashMap<String , String >> = con.hgetall (&session_key).await ?;
if let Some (session_data) = session_data {
let session = Session {
id: session_id.to_string (),
user_id: session_data.get ("user_id" ).unwrap_or (&"" .to_string ()).to_string (),
username: session_data.get ("username" ).unwrap_or (&"" .to_string ()).to_string (),
created_at: DateTime::parse_from_rfc3339 (session_data.get ("created_at" ).unwrap_or (&"" .to_string ())).unwrap ().with_timezone (&Utc),
expires_at: DateTime::parse_from_rfc3339 (session_data.get ("expires_at" ).unwrap_or (&"" .to_string ())).unwrap ().with_timezone (&Utc),
};
if session.is_expired () {
self .delete_session (session_id).await ?;
Ok (None )
} else {
Ok (Some (session))
}
} else {
Ok (None )
}
}
pub async fn delete_session (&self , session_id: &str ) -> Result <(), RedisError> {
let mut con = self .client.get_async_connection ().await ?;
let session_key = format! ("{}:{}" , self .prefix, session_id);
let session_data : Option <HashMap<String , String >> = con.hgetall (&session_key).await ?;
if let Some (session_data) = session_data {
let user_id = session_data.get ("user_id" ).unwrap_or (&"" .to_string ()).to_string ();
let user_sessions_key = format! ("{}:user:{}" , self .prefix, user_id);
con.srem (&user_sessions_key, session_id).await ?;
println! ("Successfully removed session from user sessions: {}" , session_id);
}
con.del (&session_key).await ?;
println! ("Successfully deleted session data: {}" , session_id);
let sessions_key = format! ("{}:sessions" , self .prefix);
con.srem (&sessions_key, session_id).await ?;
println! ("Successfully removed session from sessions set: {}" , session_id);
Ok (())
}
pub async fn get_user_sessions (&self , user_id: &str ) -> Result <Vec <Session>, RedisError> {
let mut con = self .client.get_async_connection ().await ?;
let user_sessions_key = format! ("{}:user:{}" , self .prefix, user_id);
let session_ids : Vec <String > = con.smembers (&user_sessions_key).await ?;
let mut sessions = Vec ::new ();
for session_id in session_ids {
if let Some (session) = self .get_session (&session_id).await ? {
sessions.push (session);
}
}
Ok (sessions)
}
pub async fn clean_expired_sessions (&self ) -> Result <(), RedisError> {
let mut con = self .client.get_async_connection ().await ?;
let sessions_key = format! ("{}:sessions" , self .prefix);
let session_ids : Vec <String > = con.smembers (&sessions_key).await ?;
for session_id in session_ids {
if let Some (session) = self .get_session (&session_id).await ? {
if session.is_expired () {
self .delete_session (&session_id).await ?;
}
}
}
println! ("Successfully cleaned expired sessions" );
Ok (())
}
}
4.3.3 定义用户认证模块
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct User {
pub id: String ,
pub username: String ,
pub password: String ,
}
impl User {
pub fn new (id: String , username: String , password: String ) -> Self {
User { id, username, password }
}
}
#[derive(Debug, Clone)]
pub struct AuthManager {
users: Vec <User>,
}
impl AuthManager {
pub fn new (users: Vec <User>) -> Self {
AuthManager { users }
}
pub fn authenticate (&self , username: &str , password: &str ) -> Option <User> {
self .users.iter ().find (|user| user.username == username && user.password == password).cloned ()
}
}
4.3.4 定义命令行界面
use clap::Parser;
use serde::{Deserialize, Serialize};
#[derive(Parser, Debug)]
#[command(name = "rust-redis-session" )]
#[command(about = "A high-performance session management system written in Rust" )]
struct Args {
#[command(subcommand)]
command: Commands,
}
#[derive(clap::Subcommand, Debug)]
enum Commands {
#[command(about = "User login" )]
Login {
#[arg(short, long, help = "Username" )]
username: String ,
#[arg(short, long, help = "Password" )]
password: String ,
},
#[command(about = "Get session by ID" )]
Get {
#[arg(short, long, help = "Session ID" )]
session_id: String ,
},
#[command(about = "Delete session by ID" )]
Delete {
#[arg(short, long, help = "Session ID" )]
session_id: String ,
},
#[command(about = "Get user sessions" )]
User {
#[arg(short, long, help = "User ID" )]
user_id: String ,
},
#[command(about = "Clean expired sessions" )]
Clean,
}
4.3.5 定义主函数
use redis::RedisError;
use std::env;
use crate::auth::AuthManager;
use crate::auth::User;
use crate::cli::Args;
use crate::session::SessionManager;
async fn connect_redis () -> Result <redis::Client, RedisError> {
let redis_url = env::var ("REDIS_URL" ).unwrap_or_else (|_| "redis://127.0.0.1:6379" .to_string ());
let client = redis::Client::open (redis_url)?;
println! ("Successfully connected to Redis" );
Ok (client)
}
async fn login (session_manager: &SessionManager, auth_manager: &AuthManager, username: &str , password: &str ) -> Result <(), RedisError> {
match auth_manager.authenticate (username, password) {
Some (user) => {
let session = session_manager.create_session (user.id, user.username).await ?;
println! ("Successfully logged in" );
println! ("Session ID: {}" , session.id);
println! ("Expires at: {}" , session.expires_at);
}
None => {
println! ("Invalid username or password" );
}
}
Ok (())
}
async fn get_session (session_manager: &SessionManager, session_id: &str ) -> Result <(), RedisError> {
match session_manager.get_session (session_id).await ? {
Some (session) => {
println! ("Successfully got session: {}" , session.id);
println! ("User ID: {}" , session.user_id);
println! ("Username: {}" , session.username);
println! ("Created at: {}" , session.created_at);
println! ("Expires at: {}" , session.expires_at);
}
None => {
println! ("Session not found or expired" );
}
}
Ok (())
}
async fn delete_session (session_manager: &SessionManager, session_id: &str ) -> Result <(), RedisError> {
session_manager.delete_session (session_id).await ?;
println! ("Successfully deleted session: {}" , session_id);
Ok (())
}
async fn get_user_sessions (session_manager: &SessionManager, user_id: &str ) -> Result <(), RedisError> {
let sessions = session_manager.get_user_sessions (user_id).await ?;
println! ("Successfully got user sessions: {}" , user_id);
for session in sessions {
println! ("Session ID: {}" , session.id);
println! ("Created at: {}" , session.created_at);
println! ("Expires at: {}" , session.expires_at);
println! ("---" );
}
Ok (())
}
async fn clean_expired_sessions (session_manager: &SessionManager) -> Result <(), RedisError> {
session_manager.clean_expired_sessions ().await ?;
Ok (())
}
#[tokio::main]
async fn main () -> Result <(), RedisError> {
let args = Args::parse ();
let client = connect_redis ().await ?;
let session_manager = SessionManager::new (client, "session" .to_string (), 3600 );
let users = vec! [
User::new ("1" , "admin" , "password123" ),
User::new ("2" , "user1" , "password456" ),
User::new ("3" , "user2" , "password789" ),
];
let auth_manager = AuthManager::new (users);
match args.command {
Commands::Login { username, password } => {
login (&session_manager, &auth_manager, &username, &password).await ?;
}
Commands::Get { session_id } => {
get_session (&session_manager, &session_id).await ?;
}
Commands::Delete { session_id } => {
delete_session (&session_manager, &session_id).await ?;
}
Commands::User { user_id } => {
get_user_sessions (&session_manager, &user_id).await ?;
}
Commands::Clean => {
clean_expired_sessions (&session_manager).await ?;
}
}
Ok (())
}
4.4 测试项目
cargo build
redis-server
cargo run -- login --username admin --password password123
cargo run -- get --session-id <session-id>
cargo run -- delete --session-id <session-id>
cargo run -- user --user-id 1
cargo run -- clean
五、性能优化
5.1 使用连接池
use redis::RedisError;
use r2d2;
use r2d2_redis::RedisConnectionManager;
async fn create_connection_pool () -> Result <r2d2::Pool<RedisConnectionManager>, RedisError> {
let redis_url = env::var ("REDIS_URL" ).unwrap_or_else (|_| "redis://127.0.0.1:6379" .to_string ());
let manager = RedisConnectionManager::new (redis_url)?;
let pool = r2d2::Pool::builder ().max_size (10 ).build (manager)?;
println! ("Successfully created Redis connection pool" );
Ok (pool)
}
#[tokio::main]
async fn main () -> Result <(), RedisError> {
let pool = create_connection_pool ().await ?;
Ok (())
}
5.2 使用批量操作
use redis::AsyncCommands;
use redis::RedisError;
use std::collections::HashMap;
impl SessionManager {
pub async fn create_sessions (&self , users: Vec <(String , String )>) -> Result <Vec <Session>, RedisError> {
let mut con = self .client.get_async_connection ().await ?;
let mut sessions = Vec ::new ();
for (user_id, username) in users {
let session = Session::new (user_id, username, self .default_expires_in);
let session_key = format! ("{}:{}" , self .prefix, session.id);
con.hset (&session_key, "user_id" , &session.user_id).await ?;
con.hset (&session_key, "username" , &session.username).await ?;
con.hset (&session_key, "created_at" , &session.created_at.to_rfc3339 ()).await ?;
con.hset (&session_key, "expires_at" , &session.expires_at.to_rfc3339 ()).await ?;
con.expire (&session_key, self .default_expires_in).await ?;
let sessions_key = format! ("{}:sessions" , self .prefix);
con.sadd (&sessions_key, &session.id).await ?;
let user_sessions_key = format! ("{}:user:{}" , self .prefix, session.user_id);
con.sadd (&user_sessions_key, &session.id).await ?;
sessions.push (session);
}
Ok (sessions)
}
}
5.3 使用管道操作
use redis::pipe;
use redis::RedisError;
impl SessionManager {
pub async fn create_sessions_pipeline (&self , users: Vec <(String , String )>) -> Result <Vec <Session>, RedisError> {
let mut con = self .client.get_async_connection ().await ?;
let mut pipe = pipe ();
let mut sessions = Vec ::new ();
for (user_id, username) in users {
let session = Session::new (user_id, username, self .default_expires_in);
let session_key = format! ("{}:{}" , self .prefix, session.id);
let sessions_key = format! ("{}:sessions" , self .prefix);
let user_sessions_key = format! ("{}:user:{}" , self .prefix, session.user_id);
pipe
.hset (&session_key, "user_id" , &session.user_id).ignore ()
.hset (&session_key, "username" , &session.username).ignore ()
.hset (&session_key, "created_at" , &session.created_at.to_rfc3339 ()).ignore ()
.hset (&session_key, "expires_at" , &session.expires_at.to_rfc3339 ()).ignore ()
.expire (&session_key, self .default_expires_in).ignore ()
.sadd (&sessions_key, &session.id).ignore ()
.sadd (&user_sessions_key, &session.id).ignore ();
sessions.push (session);
}
pipe.query_async (&mut con).await ?;
Ok (sessions)
}
}
5.4 优化缓存策略
use redis::AsyncCommands;
use redis::RedisError;
use std::collections::HashMap;
async fn optimize_cache (client: &redis::Client) -> Result <(), RedisError> {
let mut con = client.get_async_connection ().await ?;
con.set ("user:1:name" , "admin" ).await ?;
con.set ("user:1:email" , "[email protected] " ).await ?;
println! ("Successfully set user data using set commands" );
let name : String = con.get ("user:1:name" ).await ?;
let email : String = con.get ("user:1:email" ).await ?;
println! ("Successfully got user data using get commands: name={}, email={}" , name, email);
Ok (())
}
#[tokio::main]
async fn main () -> Result <(), RedisError> {
let client = connect_redis ().await ?;
optimize_cache (&client).await ?;
Ok (())
}
六、总结
6.1 技术栈
Rust :开发语言
redis-rs :Redis 的 Rust 绑定,支持异步操作
tokio :Rust 的异步运行时
serde :数据序列化/反序列化库
uuid :UUID 生成库
chrono :日期和时间处理库
thiserror :自定义错误类型库
clap :命令行参数解析库
6.2 核心功能
用户登录 :验证用户名和密码
会话创建 :创建新的会话
会话验证 :验证会话的有效性
会话过期 :自动清理过期的会话
会话统计 :统计会话数量
6.3 未来改进方向
添加会话刷新功能 :在会话即将过期时自动刷新会话的过期时间
添加会话统计功能 :统计会话的创建时间、过期时间、访问频率等
添加会话限制功能 :限制用户的会话数量
添加会话加密功能 :对会话数据进行加密存储
添加会话备份功能 :定期备份会话数据到磁盘
通过本章的学习,读者可以深入理解 Rust 与 Redis 数据库开发的工作原理和实现方法,并在实际项目中应用这些技术。同时,本章也介绍了如何优化 Redis 操作的性能,帮助读者构建高性能的应用。
微信扫一扫,关注极客日志 微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
相关免费在线工具 Base64 字符串编码/解码 将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
Base64 文件转换器 将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
Markdown 转 HTML 将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML 转 Markdown 互为补充。 在线工具,Markdown 转 HTML在线工具,online
HTML 转 Markdown 将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML 转 Markdown在线工具,online
JSON 压缩 通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online
JSON美化和格式化 将JSON字符串修饰为友好的可读格式。 在线工具,JSON美化和格式化在线工具,online