Claw 项目完整结构提交
This commit is contained in:
15
Claw/Server/shared/Cargo.toml
Normal file
15
Claw/Server/shared/Cargo.toml
Normal file
@@ -0,0 +1,15 @@
|
||||
[package]
|
||||
name = "shared"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
serde = { version = "^1.0", features = ["derive"] }
|
||||
serde_json = "^1.0"
|
||||
chrono = { version = "^0.4", features = ["serde"] }
|
||||
uuid = { version = "^1.0", features = ["v4", "serde"] }
|
||||
tokio = { version = "^1.0", features = ["full"] }
|
||||
bytes = "1.0"
|
||||
# heed = "^0.20" # 暂时移除,后续实现HeedDB功能
|
||||
# redis = "^0.29" # 暂时移除,后续实现Redis功能
|
||||
|
||||
51
Claw/Server/shared/examples/embedded_redis_usage.rs
Normal file
51
Claw/Server/shared/examples/embedded_redis_usage.rs
Normal file
@@ -0,0 +1,51 @@
|
||||
use shared::embedded_redis::{EmbeddedRedisManager, EmbeddedRedisConfig};
|
||||
use std::sync::Arc;
|
||||
|
||||
/// 嵌入式Redis使用示例
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
println!("🚀 启动嵌入式Redis服务器示例...");
|
||||
|
||||
// 配置嵌入式Redis
|
||||
let config = EmbeddedRedisConfig {
|
||||
bind: "127.0.0.1".to_string(),
|
||||
port: 6379,
|
||||
data_dir: "./embedded_redis_data".to_string(),
|
||||
persistence: true,
|
||||
max_memory: 64 * 1024 * 1024, // 64MB
|
||||
cleanup_interval: 300, // 5分钟
|
||||
};
|
||||
|
||||
// 创建管理器
|
||||
let redis_manager = Arc::new(EmbeddedRedisManager::new(config));
|
||||
|
||||
// 启动Redis服务器
|
||||
redis_manager.start().await?;
|
||||
|
||||
println!("✅ 嵌入式Redis服务器已启动!");
|
||||
println!("📍 监听地址: {}", redis_manager.get_connection_url().await);
|
||||
|
||||
// 验证服务器状态
|
||||
let is_running = redis_manager.is_running().await;
|
||||
println!("🔍 服务器运行状态: {}", if is_running { "运行中" } else { "已停止" });
|
||||
|
||||
// 获取配置信息
|
||||
let config = redis_manager.get_config().await;
|
||||
println!("📋 配置信息:");
|
||||
println!(" - 绑定地址: {}", config.bind);
|
||||
println!(" - 监听端口: {}", config.port);
|
||||
println!(" - 数据目录: {}", config.data_dir);
|
||||
println!(" - 持久化: {}", if config.persistence { "启用" } else { "禁用" });
|
||||
println!(" - 最大内存: {}MB", config.max_memory / (1024 * 1024));
|
||||
|
||||
// 保持服务器运行一段时间
|
||||
println!("⏰ 服务器将在5秒后停止...");
|
||||
tokio::time::sleep(tokio::time::Duration::from_secs(5)).await;
|
||||
|
||||
// 停止Redis服务器
|
||||
redis_manager.stop().await;
|
||||
|
||||
println!("✅ 嵌入式Redis服务器已停止!");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
745
Claw/Server/shared/src/embedded_redis.rs
Normal file
745
Claw/Server/shared/src/embedded_redis.rs
Normal file
@@ -0,0 +1,745 @@
|
||||
use tokio::net::{TcpListener, TcpStream};
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::{Mutex, RwLock};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use std::path::Path;
|
||||
use std::fs;
|
||||
use std::time::{SystemTime, Duration, UNIX_EPOCH};
|
||||
|
||||
/// 嵌入式Redis配置
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct EmbeddedRedisConfig {
|
||||
pub bind: String,
|
||||
pub port: u16,
|
||||
pub data_dir: String,
|
||||
pub persistence: bool,
|
||||
pub max_memory: usize,
|
||||
pub cleanup_interval: u64, // 秒
|
||||
}
|
||||
|
||||
impl Default for EmbeddedRedisConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
bind: "127.0.0.1".to_string(),
|
||||
port: 6379,
|
||||
data_dir: "./embedded_redis_data".to_string(),
|
||||
persistence: true,
|
||||
max_memory: 64 * 1024 * 1024, // 64MB
|
||||
cleanup_interval: 300, // 5分钟
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 键值对数据结构
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
struct RedisValue {
|
||||
data: Vec<u8>,
|
||||
expires_at: Option<u64>, // Unix时间戳(秒)
|
||||
}
|
||||
|
||||
impl RedisValue {
|
||||
fn new(data: Vec<u8>) -> Self {
|
||||
Self {
|
||||
data,
|
||||
expires_at: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn with_expiry(data: Vec<u8>, ttl_seconds: u64) -> Self {
|
||||
let expires_at = SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_secs() + ttl_seconds;
|
||||
|
||||
Self {
|
||||
data,
|
||||
expires_at: Some(expires_at),
|
||||
}
|
||||
}
|
||||
|
||||
fn is_expired(&self) -> bool {
|
||||
if let Some(expires_at) = self.expires_at {
|
||||
let now = SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_secs();
|
||||
now >= expires_at
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 嵌入式Redis数据库
|
||||
pub struct EmbeddedRedisDb {
|
||||
data: RwLock<HashMap<String, RedisValue>>,
|
||||
config: EmbeddedRedisConfig,
|
||||
}
|
||||
|
||||
impl EmbeddedRedisDb {
|
||||
fn new(config: EmbeddedRedisConfig) -> Self {
|
||||
let mut db = Self {
|
||||
data: RwLock::new(HashMap::new()),
|
||||
config,
|
||||
};
|
||||
|
||||
// 如果启用了持久化,加载数据
|
||||
if db.config.persistence {
|
||||
let _ = db.load_from_disk();
|
||||
}
|
||||
|
||||
db
|
||||
}
|
||||
|
||||
/// 从磁盘加载数据
|
||||
async fn load_from_disk(&mut self) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let data_path = Path::new(&self.config.data_dir).join("redis_data.json");
|
||||
|
||||
if data_path.exists() {
|
||||
let data = fs::read_to_string(&data_path)?;
|
||||
let loaded_data: HashMap<String, RedisValue> = serde_json::from_str(&data)?;
|
||||
|
||||
let mut db = self.data.write().await;
|
||||
*db = loaded_data;
|
||||
|
||||
println!("✅ 从磁盘加载 {} 个键值对", db.len());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 保存到磁盘
|
||||
async fn save_to_disk(&self) -> Result<(), Box<dyn std::error::Error>> {
|
||||
if !self.config.persistence {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// 确保数据目录存在
|
||||
fs::create_dir_all(&self.config.data_dir)?;
|
||||
|
||||
let data_path = Path::new(&self.config.data_dir).join("redis_data.json");
|
||||
let temp_path = Path::new(&self.config.data_dir).join("redis_data.json.tmp");
|
||||
|
||||
let db = self.data.read().await;
|
||||
let data = serde_json::to_string_pretty(&*db)?;
|
||||
|
||||
// 先写入临时文件
|
||||
fs::write(&temp_path, data)?;
|
||||
|
||||
// 原子性重命名
|
||||
fs::rename(&temp_path, &data_path)?;
|
||||
|
||||
println!("💾 保存 {} 个键值对到磁盘", db.len());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 清理过期键
|
||||
async fn cleanup_expired(&self) {
|
||||
let mut db = self.data.write().await;
|
||||
let keys_to_remove: Vec<String> = db
|
||||
.iter()
|
||||
.filter_map(|(k, v)| if v.is_expired() { Some(k.clone()) } else { None })
|
||||
.collect();
|
||||
|
||||
for key in keys_to_remove {
|
||||
db.remove(&key);
|
||||
println!("🧹 清理过期键: {}", key);
|
||||
}
|
||||
}
|
||||
|
||||
/// 获取值
|
||||
async fn get(&self, key: &str) -> Option<Vec<u8>> {
|
||||
let db = self.data.read().await;
|
||||
|
||||
if let Some(value) = db.get(key) {
|
||||
if !value.is_expired() {
|
||||
Some(value.data.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// 设置值
|
||||
async fn set(&self, key: String, value: Vec<u8>) {
|
||||
let mut db = self.data.write().await;
|
||||
db.insert(key, RedisValue::new(value));
|
||||
|
||||
// 异步保存到磁盘(简化实现)
|
||||
if self.config.persistence {
|
||||
// 这里可以添加异步保存逻辑
|
||||
// 为了简化,暂时不实现自动保存
|
||||
}
|
||||
}
|
||||
|
||||
/// 设置带过期时间的值
|
||||
async fn setex(&self, key: String, value: Vec<u8>, ttl_seconds: u64) {
|
||||
let mut db = self.data.write().await;
|
||||
db.insert(key, RedisValue::with_expiry(value, ttl_seconds));
|
||||
}
|
||||
|
||||
/// 删除键
|
||||
async fn del(&self, keys: &[String]) -> i64 {
|
||||
let mut db = self.data.write().await;
|
||||
let mut count = 0;
|
||||
|
||||
for key in keys {
|
||||
if db.remove(key).is_some() {
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
count
|
||||
}
|
||||
|
||||
/// 检查键是否存在
|
||||
async fn exists(&self, key: &str) -> bool {
|
||||
let db = self.data.read().await;
|
||||
|
||||
if let Some(value) = db.get(key) {
|
||||
!value.is_expired()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// 获取所有键
|
||||
async fn keys(&self, pattern: &str) -> Vec<String> {
|
||||
let db = self.data.read().await;
|
||||
|
||||
db.keys()
|
||||
.filter(|key| {
|
||||
// 简单的通配符匹配
|
||||
if pattern == "*" {
|
||||
true
|
||||
} else if pattern.ends_with("*") {
|
||||
let prefix = &pattern[..pattern.len() - 1];
|
||||
key.starts_with(prefix)
|
||||
} else {
|
||||
*key == pattern
|
||||
}
|
||||
})
|
||||
.cloned()
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// 获取TTL
|
||||
async fn ttl(&self, key: &str) -> i64 {
|
||||
let db = self.data.read().await;
|
||||
|
||||
if let Some(value) = db.get(key) {
|
||||
if let Some(expires_at) = value.expires_at {
|
||||
let now = SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_secs();
|
||||
|
||||
if now < expires_at {
|
||||
(expires_at - now) as i64
|
||||
} else {
|
||||
-2 // 键已过期
|
||||
}
|
||||
} else {
|
||||
-1 // 键永不过期
|
||||
}
|
||||
} else {
|
||||
-2 // 键不存在
|
||||
}
|
||||
}
|
||||
|
||||
/// 获取数据库大小
|
||||
async fn dbsize(&self) -> usize {
|
||||
let db = self.data.read().await;
|
||||
db.len()
|
||||
}
|
||||
|
||||
/// 清空数据库
|
||||
async fn flushdb(&self) {
|
||||
let mut db = self.data.write().await;
|
||||
db.clear();
|
||||
println!("🗑️ 清空数据库");
|
||||
}
|
||||
}
|
||||
|
||||
/// 嵌入式Redis服务器
|
||||
pub struct EmbeddedRedisServer {
|
||||
config: EmbeddedRedisConfig,
|
||||
listener: Option<TcpListener>,
|
||||
db: Arc<EmbeddedRedisDb>,
|
||||
running: Arc<Mutex<bool>>,
|
||||
}
|
||||
|
||||
impl EmbeddedRedisServer {
|
||||
/// 创建新的嵌入式Redis服务器
|
||||
pub fn new(config: EmbeddedRedisConfig) -> Self {
|
||||
let db = Arc::new(EmbeddedRedisDb::new(config.clone()));
|
||||
|
||||
Self {
|
||||
config,
|
||||
listener: None,
|
||||
db,
|
||||
running: Arc::new(Mutex::new(false)),
|
||||
}
|
||||
}
|
||||
|
||||
/// 启动嵌入式Redis服务器
|
||||
pub async fn start(&mut self) -> Result<(), Box<dyn std::error::Error>> {
|
||||
use std::net::SocketAddr;
|
||||
let addr: SocketAddr = format!("{}:{}", self.config.bind, self.config.port).parse()?;
|
||||
|
||||
println!("🚀 启动嵌入式Redis服务器...");
|
||||
println!("📍 绑定地址: {}", addr);
|
||||
println!("💾 数据目录: {}", self.config.data_dir);
|
||||
println!("💾 持久化: {}", if self.config.persistence { "启用" } else { "禁用" });
|
||||
println!("💾 最大内存: {}MB", self.config.max_memory / (1024 * 1024));
|
||||
|
||||
// 创建TCP监听器
|
||||
let listener = TcpListener::bind(addr).await?;
|
||||
self.listener = Some(listener);
|
||||
|
||||
// 设置运行状态
|
||||
*self.running.lock().await = true;
|
||||
|
||||
println!("✅ 嵌入式Redis服务器启动成功!监听地址: {}", addr);
|
||||
|
||||
// 启动后台任务
|
||||
self.start_background_tasks().await;
|
||||
|
||||
// 启动接受连接的任务
|
||||
self.accept_connections();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 启动后台任务
|
||||
async fn start_background_tasks(&self) {
|
||||
let db = self.db.clone();
|
||||
let running = self.running.clone();
|
||||
let cleanup_interval = self.config.cleanup_interval;
|
||||
let persistence = self.config.persistence;
|
||||
|
||||
// 清理过期键任务
|
||||
tokio::spawn(async move {
|
||||
while *running.lock().await {
|
||||
db.cleanup_expired().await;
|
||||
tokio::time::sleep(Duration::from_secs(cleanup_interval)).await;
|
||||
}
|
||||
});
|
||||
|
||||
// 如果启用了持久化,启动定期保存任务
|
||||
if persistence {
|
||||
let db = self.db.clone();
|
||||
let running = self.running.clone();
|
||||
|
||||
tokio::spawn(async move {
|
||||
while *running.lock().await {
|
||||
tokio::time::sleep(Duration::from_secs(60)).await;
|
||||
if let Err(e) = db.save_to_disk().await {
|
||||
eprintln!("❌ 自动保存失败: {}", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// 接受连接
|
||||
fn accept_connections(&self) {
|
||||
if let Some(listener) = &self.listener {
|
||||
let db = self.db.clone();
|
||||
let running = self.running.clone();
|
||||
|
||||
// 创建一个新的监听器用于异步任务
|
||||
let addr = listener.local_addr().expect("Failed to get local address");
|
||||
|
||||
tokio::spawn(async move {
|
||||
// 重新绑定监听器
|
||||
match TcpListener::bind(addr).await {
|
||||
Ok(new_listener) => {
|
||||
loop {
|
||||
if !*running.lock().await {
|
||||
break;
|
||||
}
|
||||
|
||||
match new_listener.accept().await {
|
||||
Ok((socket, addr)) => {
|
||||
println!("🔗 新连接来自: {}", addr);
|
||||
let db = db.clone();
|
||||
let running = running.clone();
|
||||
|
||||
tokio::spawn(async move {
|
||||
if let Err(e) = handle_connection(socket, db, running).await {
|
||||
eprintln!("❌ 连接处理错误: {}", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("❌ 接受连接错误: {}", e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("❌ 重新绑定监听器失败: {}", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// 获取Redis连接URL
|
||||
pub fn get_connection_url(&self) -> String {
|
||||
format!("redis://{}:{}/", self.config.bind, self.config.port)
|
||||
}
|
||||
|
||||
/// 获取配置
|
||||
pub fn get_config(&self) -> &EmbeddedRedisConfig {
|
||||
&self.config
|
||||
}
|
||||
|
||||
/// 停止服务器
|
||||
pub async fn stop(&mut self) {
|
||||
println!("🛑 停止嵌入式Redis服务器...");
|
||||
|
||||
// 设置停止标志
|
||||
*self.running.lock().await = false;
|
||||
|
||||
// 保存数据到磁盘
|
||||
if self.config.persistence {
|
||||
if let Err(e) = self.db.save_to_disk().await {
|
||||
eprintln!("❌ 保存数据失败: {}", e);
|
||||
}
|
||||
}
|
||||
|
||||
self.listener = None;
|
||||
println!("✅ 嵌入式Redis服务器已停止");
|
||||
}
|
||||
|
||||
/// 获取数据库引用(供内部使用)
|
||||
pub fn get_db(&self) -> Arc<EmbeddedRedisDb> {
|
||||
self.db.clone()
|
||||
}
|
||||
}
|
||||
|
||||
/// 处理单个连接
|
||||
async fn handle_connection(
|
||||
mut socket: TcpStream,
|
||||
db: Arc<EmbeddedRedisDb>,
|
||||
running: Arc<Mutex<bool>>,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader};
|
||||
|
||||
let (reader, mut writer) = socket.split();
|
||||
let mut reader = BufReader::new(reader);
|
||||
let mut buffer = String::new();
|
||||
|
||||
loop {
|
||||
if !*running.lock().await {
|
||||
break;
|
||||
}
|
||||
|
||||
buffer.clear();
|
||||
match reader.read_line(&mut buffer).await {
|
||||
Ok(0) => break, // 连接关闭
|
||||
Ok(_) => {
|
||||
let line = buffer.trim();
|
||||
if line.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let response = match parse_and_execute_command(line, &db).await {
|
||||
Ok(resp) => resp,
|
||||
Err(e) => format!("-ERR {}\r\n", e),
|
||||
};
|
||||
|
||||
writer.write_all(response.as_bytes()).await?;
|
||||
writer.flush().await?;
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("❌ 读取错误: {}", e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 解析并执行命令
|
||||
async fn parse_and_execute_command(
|
||||
line: &str,
|
||||
db: &Arc<EmbeddedRedisDb>,
|
||||
) -> Result<String, Box<dyn std::error::Error>> {
|
||||
// 解析Redis协议格式
|
||||
let parts = parse_redis_protocol(line)?;
|
||||
if parts.is_empty() {
|
||||
return Ok("+OK\r\n".to_string());
|
||||
}
|
||||
|
||||
let command = parts[0].to_uppercase();
|
||||
|
||||
match command.as_str() {
|
||||
"PING" => {
|
||||
if parts.len() > 1 {
|
||||
Ok(format!("+{}\r\n", parts[1..].join(" ")))
|
||||
} else {
|
||||
Ok("+PONG\r\n".to_string())
|
||||
}
|
||||
}
|
||||
"GET" => {
|
||||
if parts.len() != 2 {
|
||||
return Err("wrong number of arguments for 'get' command".into());
|
||||
}
|
||||
let key = &parts[1];
|
||||
match db.get(key).await {
|
||||
Some(value) => {
|
||||
let value_str = String::from_utf8_lossy(&value);
|
||||
Ok(format!("${}\r\n{}\r\n", value_str.len(), value_str))
|
||||
}
|
||||
None => Ok("$-1\r\n".to_string()),
|
||||
}
|
||||
}
|
||||
"SET" => {
|
||||
if parts.len() < 3 {
|
||||
return Err("wrong number of arguments for 'set' command".into());
|
||||
}
|
||||
let key = &parts[1];
|
||||
let value = &parts[2];
|
||||
|
||||
db.set(key.clone(), value.as_bytes().to_vec()).await;
|
||||
Ok("+OK\r\n".to_string())
|
||||
}
|
||||
"SETEX" => {
|
||||
if parts.len() != 4 {
|
||||
return Err("wrong number of arguments for 'setex' command".into());
|
||||
}
|
||||
let key = &parts[1];
|
||||
let ttl: u64 = parts[2].parse().map_err(|_| "invalid expire time")?;
|
||||
let value = &parts[3];
|
||||
|
||||
db.setex(key.clone(), value.as_bytes().to_vec(), ttl).await;
|
||||
Ok("+OK\r\n".to_string())
|
||||
}
|
||||
"DEL" => {
|
||||
if parts.len() < 2 {
|
||||
return Err("wrong number of arguments for 'del' command".into());
|
||||
}
|
||||
let keys: Vec<String> = parts[1..].to_vec();
|
||||
let count = db.del(&keys).await;
|
||||
Ok(format!(":{}\r\n", count))
|
||||
}
|
||||
"EXISTS" => {
|
||||
if parts.len() != 2 {
|
||||
return Err("wrong number of arguments for 'exists' command".into());
|
||||
}
|
||||
let key = &parts[1];
|
||||
if db.exists(key).await {
|
||||
Ok(":1\r\n".to_string())
|
||||
} else {
|
||||
Ok(":0\r\n".to_string())
|
||||
}
|
||||
}
|
||||
"KEYS" => {
|
||||
let pattern = if parts.len() > 1 { &parts[1] } else { "*" };
|
||||
let keys = db.keys(pattern).await;
|
||||
|
||||
if keys.is_empty() {
|
||||
Ok("*0\r\n".to_string())
|
||||
} else {
|
||||
let mut response = format!("*{}\r\n", keys.len());
|
||||
for key in keys {
|
||||
response.push_str(&format!("${}\r\n{}\r\n", key.len(), key));
|
||||
}
|
||||
Ok(response)
|
||||
}
|
||||
}
|
||||
"TTL" => {
|
||||
if parts.len() != 2 {
|
||||
return Err("wrong number of arguments for 'ttl' command".into());
|
||||
}
|
||||
let key = &parts[1];
|
||||
let ttl = db.ttl(key).await;
|
||||
Ok(format!(":{}\r\n", ttl))
|
||||
}
|
||||
"DBSIZE" => {
|
||||
let size = db.dbsize().await;
|
||||
Ok(format!(":{}\r\n", size))
|
||||
}
|
||||
"FLUSHDB" => {
|
||||
db.flushdb().await;
|
||||
Ok("+OK\r\n".to_string())
|
||||
}
|
||||
"INFO" => {
|
||||
let info = format!(
|
||||
"# Server\r\n\
|
||||
redis_version:embedded-2.0\r\n\
|
||||
redis_mode:standalone\r\n\
|
||||
tcp_port:{}\r\n\
|
||||
\r\n\
|
||||
# Clients\r\n\
|
||||
connected_clients:1\r\n\
|
||||
\r\n\
|
||||
# Memory\r\n\
|
||||
used_memory:{}\r\n\
|
||||
maxmemory:{}\r\n\
|
||||
\r\n\
|
||||
# Persistence\r\n\
|
||||
rdb_last_save_time:{}\r\n\
|
||||
\r\n\
|
||||
# Stats\r\n\
|
||||
total_connections_received:1\r\n\
|
||||
total_commands_processed:1\r\n\
|
||||
\r\n",
|
||||
6379,
|
||||
1024,
|
||||
64 * 1024 * 1024,
|
||||
SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs()
|
||||
);
|
||||
Ok(format!("${}\r\n{}\r\n", info.len(), info))
|
||||
}
|
||||
"QUIT" => {
|
||||
Ok("+OK\r\n".to_string())
|
||||
}
|
||||
_ => {
|
||||
Err(format!("unknown command '{}'", command).into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 解析Redis协议(简化版)
|
||||
fn parse_redis_protocol(line: &str) -> Result<Vec<String>, Box<dyn std::error::Error>> {
|
||||
// 这里实现简化的Redis协议解析
|
||||
// 实际应该解析RESP协议,这里为了简化,直接按空格分割
|
||||
Ok(line.split_whitespace().map(|s| s.to_string()).collect())
|
||||
}
|
||||
|
||||
/// 嵌入式Redis管理器
|
||||
pub struct EmbeddedRedisManager {
|
||||
server: Arc<Mutex<EmbeddedRedisServer>>,
|
||||
}
|
||||
|
||||
impl EmbeddedRedisManager {
|
||||
/// 创建新的管理器
|
||||
pub fn new(config: EmbeddedRedisConfig) -> Self {
|
||||
Self {
|
||||
server: Arc::new(Mutex::new(EmbeddedRedisServer::new(config))),
|
||||
}
|
||||
}
|
||||
|
||||
/// 启动Redis服务器
|
||||
pub async fn start(&self) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut server = self.server.lock().await;
|
||||
server.start().await
|
||||
}
|
||||
|
||||
/// 停止Redis服务器
|
||||
pub async fn stop(&self) {
|
||||
let mut server = self.server.lock().await;
|
||||
server.stop().await;
|
||||
}
|
||||
|
||||
/// 获取Redis连接URL
|
||||
pub async fn get_connection_url(&self) -> String {
|
||||
let server = self.server.lock().await;
|
||||
server.get_connection_url()
|
||||
}
|
||||
|
||||
/// 获取配置
|
||||
pub async fn get_config(&self) -> EmbeddedRedisConfig {
|
||||
let server = self.server.lock().await;
|
||||
server.get_config().clone()
|
||||
}
|
||||
|
||||
/// 检查是否运行中
|
||||
pub async fn is_running(&self) -> bool {
|
||||
let server = self.server.lock().await;
|
||||
*server.running.lock().await
|
||||
}
|
||||
|
||||
/// 获取数据库引用(供高级操作使用)
|
||||
pub async fn get_db(&self) -> Arc<EmbeddedRedisDb> {
|
||||
let server = self.server.lock().await;
|
||||
server.get_db()
|
||||
}
|
||||
|
||||
/// 手动保存到磁盘
|
||||
pub async fn save(&self) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let server = self.server.lock().await;
|
||||
server.get_db().save_to_disk().await
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for EmbeddedRedisManager {
|
||||
fn drop(&mut self) {
|
||||
// 在析构时尝试停止服务器并保存数据
|
||||
let server = self.server.clone();
|
||||
tokio::spawn(async move {
|
||||
let mut server = server.lock().await;
|
||||
server.stop().await;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_embedded_redis_config() {
|
||||
let config = EmbeddedRedisConfig::default();
|
||||
assert_eq!(config.port, 6379);
|
||||
assert_eq!(config.max_memory, 64 * 1024 * 1024);
|
||||
assert!(config.persistence);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_embedded_redis_manager() {
|
||||
let config = EmbeddedRedisConfig {
|
||||
port: 16379, // 使用非标准端口避免冲突
|
||||
data_dir: "./test_redis_data".to_string(),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let manager = EmbeddedRedisManager::new(config);
|
||||
assert_eq!(manager.get_connection_url().await, "redis://127.0.0.1:16379/");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_redis_commands() {
|
||||
let config = EmbeddedRedisConfig {
|
||||
port: 16380,
|
||||
data_dir: "./test_redis_commands".to_string(),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let db = Arc::new(EmbeddedRedisDb::new(config));
|
||||
|
||||
// 测试SET和GET
|
||||
db.set("test_key".to_string(), b"test_value".to_vec()).await;
|
||||
let value = db.get("test_key").await;
|
||||
assert_eq!(value, Some(b"test_value".to_vec()));
|
||||
|
||||
// 测试EXISTS
|
||||
assert!(db.exists("test_key").await);
|
||||
assert!(!db.exists("non_existent").await);
|
||||
|
||||
// 测试DEL
|
||||
let deleted = db.del(&["test_key".to_string()]).await;
|
||||
assert_eq!(deleted, 1);
|
||||
assert!(!db.exists("test_key").await);
|
||||
|
||||
// 测试KEYS
|
||||
db.set("key1".to_string(), b"value1".to_vec()).await;
|
||||
db.set("key2".to_string(), b"value2".to_vec()).await;
|
||||
let keys = db.keys("*").await;
|
||||
assert!(keys.contains(&"key1".to_string()));
|
||||
assert!(keys.contains(&"key2".to_string()));
|
||||
|
||||
// 清理测试数据
|
||||
db.flushdb().await;
|
||||
}
|
||||
}
|
||||
293
Claw/Server/shared/src/lib.rs
Normal file
293
Claw/Server/shared/src/lib.rs
Normal file
@@ -0,0 +1,293 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
|
||||
pub mod embedded_redis;
|
||||
pub use embedded_redis::{EmbeddedRedisServer, EmbeddedRedisConfig, EmbeddedRedisManager};
|
||||
|
||||
/// 任务类型枚举
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub enum TaskType {
|
||||
/// 文本处理任务
|
||||
TextProcessing,
|
||||
/// 数据分析任务
|
||||
DataAnalysis,
|
||||
/// AI对话任务
|
||||
AIChat,
|
||||
/// 文件处理任务
|
||||
FileProcessing,
|
||||
/// 自定义任务
|
||||
Custom(String),
|
||||
}
|
||||
|
||||
impl fmt::Display for TaskType {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
TaskType::TextProcessing => write!(f, "text_processing"),
|
||||
TaskType::DataAnalysis => write!(f, "data_analysis"),
|
||||
TaskType::AIChat => write!(f, "ai_chat"),
|
||||
TaskType::FileProcessing => write!(f, "file_processing"),
|
||||
TaskType::Custom(s) => write!(f, "custom_{}", s),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 任务状态枚举
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub enum TaskStatus {
|
||||
/// 待处理
|
||||
Pending,
|
||||
/// 处理中
|
||||
Processing,
|
||||
/// 已完成
|
||||
Completed,
|
||||
/// 失败
|
||||
Failed,
|
||||
/// 已取消
|
||||
Cancelled,
|
||||
}
|
||||
|
||||
impl fmt::Display for TaskStatus {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
TaskStatus::Pending => write!(f, "pending"),
|
||||
TaskStatus::Processing => write!(f, "processing"),
|
||||
TaskStatus::Completed => write!(f, "completed"),
|
||||
TaskStatus::Failed => write!(f, "failed"),
|
||||
TaskStatus::Cancelled => write!(f, "cancelled"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 任务请求数据结构
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct TaskRequest {
|
||||
/// 用户ID
|
||||
pub user_id: String,
|
||||
/// 任务类型
|
||||
pub task_type: TaskType,
|
||||
/// 任务内容
|
||||
pub content: String,
|
||||
/// 时间戳
|
||||
pub timestamp: i64,
|
||||
/// 优先级 (1-10, 数字越大优先级越高)
|
||||
pub priority: u8,
|
||||
/// 超时时间(秒)
|
||||
pub timeout: Option<u64>,
|
||||
/// 额外参数
|
||||
pub extra_params: Option<serde_json::Value>,
|
||||
}
|
||||
|
||||
/// 任务响应数据结构
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct TaskResponse {
|
||||
/// 是否成功
|
||||
pub success: bool,
|
||||
/// 消息
|
||||
pub message: String,
|
||||
/// 任务ID
|
||||
pub task_id: Option<String>,
|
||||
/// 结果数据
|
||||
pub result: Option<serde_json::Value>,
|
||||
/// 处理时间(毫秒)
|
||||
pub processing_time: Option<u64>,
|
||||
/// 错误信息
|
||||
pub error: Option<String>,
|
||||
}
|
||||
|
||||
/// 任务状态数据结构
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct TaskStatusResponse {
|
||||
/// 任务ID
|
||||
pub task_id: String,
|
||||
/// 任务状态
|
||||
pub status: TaskStatus,
|
||||
/// 进度 (0-100)
|
||||
pub progress: u8,
|
||||
/// 状态消息
|
||||
pub status_message: String,
|
||||
/// 创建时间
|
||||
pub created_at: i64,
|
||||
/// 更新时间
|
||||
pub updated_at: i64,
|
||||
/// 结果数据
|
||||
pub result: Option<serde_json::Value>,
|
||||
}
|
||||
|
||||
/// 服务健康检查响应
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct HealthResponse {
|
||||
/// 服务状态
|
||||
pub status: String,
|
||||
/// 服务名称
|
||||
pub service: String,
|
||||
/// 时间戳
|
||||
pub timestamp: i64,
|
||||
/// 版本号
|
||||
pub version: String,
|
||||
/// 额外信息
|
||||
pub extra: Option<serde_json::Value>,
|
||||
}
|
||||
|
||||
/// 错误响应数据结构
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ErrorResponse {
|
||||
/// 错误代码
|
||||
pub code: String,
|
||||
/// 错误消息
|
||||
pub message: String,
|
||||
/// 详细错误信息
|
||||
pub details: Option<String>,
|
||||
/// 时间戳
|
||||
pub timestamp: i64,
|
||||
}
|
||||
|
||||
/// WebSocket 消息类型
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub enum WebSocketMessage {
|
||||
/// 任务请求
|
||||
TaskRequest(TaskRequest),
|
||||
/// 任务响应
|
||||
TaskResponse(TaskResponse),
|
||||
/// 任务状态更新
|
||||
TaskStatusUpdate(TaskStatusResponse),
|
||||
/// 心跳消息
|
||||
Heartbeat,
|
||||
/// 错误消息
|
||||
Error(ErrorResponse),
|
||||
}
|
||||
|
||||
/// 企业微信消息数据结构
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct WeChatMessage {
|
||||
/// 消息ID
|
||||
pub msg_id: String,
|
||||
/// 发送者
|
||||
pub from_user: String,
|
||||
/// 接收者
|
||||
pub to_user: String,
|
||||
/// 消息类型
|
||||
pub msg_type: String,
|
||||
/// 消息内容
|
||||
pub content: String,
|
||||
/// 时间戳
|
||||
pub timestamp: i64,
|
||||
}
|
||||
|
||||
/// 微信小程序消息数据结构
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct MiniProgramMessage {
|
||||
/// 用户ID
|
||||
pub user_id: String,
|
||||
/// 消息类型
|
||||
pub msg_type: String,
|
||||
/// 消息内容
|
||||
pub content: String,
|
||||
/// 时间戳
|
||||
pub timestamp: i64,
|
||||
/// 额外参数
|
||||
pub extra: Option<serde_json::Value>,
|
||||
}
|
||||
|
||||
/// 常量定义
|
||||
pub mod constants {
|
||||
/// 默认任务超时时间(秒)
|
||||
pub const DEFAULT_TASK_TIMEOUT: u64 = 300;
|
||||
|
||||
/// 最大任务优先级
|
||||
pub const MAX_TASK_PRIORITY: u8 = 10;
|
||||
|
||||
/// 最小任务优先级
|
||||
pub const MIN_TASK_PRIORITY: u8 = 1;
|
||||
|
||||
/// 默认服务端口
|
||||
pub const DEFAULT_GATEWAY_PORT: u16 = 3000;
|
||||
pub const DEFAULT_SMARTCLAW_PORT: u16 = 3001;
|
||||
|
||||
/// WebSocket 心跳间隔(秒)
|
||||
pub const WEBSOCKET_HEARTBEAT_INTERVAL: u64 = 30;
|
||||
|
||||
/// WebSocket 超时时间(秒)
|
||||
pub const WEBSOCKET_TIMEOUT: u64 = 300;
|
||||
}
|
||||
|
||||
/// 工具函数
|
||||
pub mod utils {
|
||||
use super::*;
|
||||
use chrono::Utc;
|
||||
|
||||
/// 生成任务ID
|
||||
pub fn generate_task_id(user_id: &str) -> String {
|
||||
format!("task_{}_{}", user_id, Utc::now().timestamp_millis())
|
||||
}
|
||||
|
||||
/// 生成消息ID
|
||||
pub fn generate_msg_id() -> String {
|
||||
format!("msg_{}", Utc::now().timestamp_millis())
|
||||
}
|
||||
|
||||
/// 验证任务优先级
|
||||
pub fn validate_priority(priority: u8) -> u8 {
|
||||
priority.clamp(constants::MIN_TASK_PRIORITY, constants::MAX_TASK_PRIORITY)
|
||||
}
|
||||
|
||||
/// 获取当前时间戳
|
||||
pub fn current_timestamp() -> i64 {
|
||||
Utc::now().timestamp()
|
||||
}
|
||||
|
||||
/// 创建成功响应
|
||||
pub fn create_success_response(message: &str, task_id: Option<String>, result: Option<serde_json::Value>) -> TaskResponse {
|
||||
TaskResponse {
|
||||
success: true,
|
||||
message: message.to_string(),
|
||||
task_id,
|
||||
result,
|
||||
processing_time: None,
|
||||
error: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// 创建错误响应
|
||||
pub fn create_error_response(message: &str, error: Option<String>) -> TaskResponse {
|
||||
TaskResponse {
|
||||
success: false,
|
||||
message: message.to_string(),
|
||||
task_id: None,
|
||||
result: None,
|
||||
processing_time: None,
|
||||
error,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_task_type_display() {
|
||||
assert_eq!(TaskType::TextProcessing.to_string(), "text_processing");
|
||||
assert_eq!(TaskType::DataAnalysis.to_string(), "data_analysis");
|
||||
assert_eq!(TaskType::Custom("test".to_string()).to_string(), "custom_test");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_task_status_display() {
|
||||
assert_eq!(TaskStatus::Pending.to_string(), "pending");
|
||||
assert_eq!(TaskStatus::Completed.to_string(), "completed");
|
||||
assert_eq!(TaskStatus::Failed.to_string(), "failed");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_generate_task_id() {
|
||||
let task_id = utils::generate_task_id("user123");
|
||||
assert!(task_id.starts_with("task_user123_"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_validate_priority() {
|
||||
assert_eq!(utils::validate_priority(0), 1);
|
||||
assert_eq!(utils::validate_priority(5), 5);
|
||||
assert_eq!(utils::validate_priority(15), 10);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user