1use crate::config::RedisConfig;
24use redis::aio::ConnectionManager;
25use redis::AsyncCommands;
26use serde::{de::DeserializeOwned, Serialize};
27use std::sync::Arc;
28
29pub const INVALIDATE_CHANNEL: &str = "cache:invalidate";
31
32#[derive(Clone)]
36pub struct RedisCache {
37 client: redis::Client,
39 conn: ConnectionManager,
41 key_prefix: Arc<String>,
43 default_ttl_secs: u64,
45 sender_id: Arc<String>,
47}
48
49impl RedisCache {
50 pub async fn connect(cfg: &RedisConfig) -> anyhow::Result<Self> {
62 let client = redis::Client::open(cfg.url.as_str())
63 .map_err(|e| anyhow::anyhow!("打开 Redis 客户端失败({}): {e}", cfg.url))?;
64 let mut conn = client
65 .get_connection_manager()
66 .await
67 .map_err(|e| anyhow::anyhow!("连接 Redis 失败({}): {e}", cfg.url))?;
68 redis::cmd("PING")
70 .query_async::<String>(&mut conn)
71 .await
72 .map_err(|e| anyhow::anyhow!("Redis PING 失败({}): {e}", cfg.url))?;
73 Ok(Self {
74 client,
75 conn,
76 key_prefix: Arc::new(cfg.key_prefix.clone()),
77 default_ttl_secs: cfg.default_ttl_secs,
78 sender_id: Arc::new(uuid::Uuid::new_v4().to_string()),
79 })
80 }
81
82 pub fn sender_id(&self) -> &str {
84 &self.sender_id
85 }
86
87 pub fn default_ttl_secs(&self) -> u64 {
89 self.default_ttl_secs
90 }
91
92 pub fn full_key(&self, logical: &str) -> String {
94 format!("{}:{}", self.key_prefix, logical)
95 }
96
97 pub fn json_cache_key(key: &str) -> String {
99 format!("cache:json:{key}")
100 }
101
102 pub fn value_cache_key(namespace: &str, key: &str) -> String {
104 format!("cache:value:{namespace}:{key}")
105 }
106
107 pub async fn get_bytes(&self, logical: &str) -> Option<Vec<u8>> {
111 let key = self.full_key(logical);
112 let mut conn = self.conn.clone();
113 match conn.get::<_, Option<Vec<u8>>>(&key).await {
114 Ok(v) => v,
115 Err(e) => {
116 tracing::warn!("Redis GET 失败 key={key}: {e}");
117 None
118 }
119 }
120 }
121
122 pub async fn set_bytes_ttl(&self, logical: &str, value: &[u8], ttl_secs: u64) {
126 let key = self.full_key(logical);
127 let mut conn = self.conn.clone();
128 if let Err(e) = conn.set_ex::<_, _, ()>(&key, value, ttl_secs).await {
129 tracing::warn!("Redis SETEX 失败 key={key}: {e}");
130 }
131 }
132
133 pub async fn del(&self, logical: &str) {
135 let key = self.full_key(logical);
136 let mut conn = self.conn.clone();
137 if let Err(e) = conn.del::<_, ()>(&key).await {
138 tracing::warn!("Redis DEL 失败 key={key}: {e}");
139 }
140 }
141
142 pub async fn del_pattern(&self, logical_pattern: &str) {
146 let pattern = self.full_key(logical_pattern);
147 let mut conn = self.conn.clone();
148 let mut cursor = 0_u64;
149 loop {
150 let scan = redis::cmd("SCAN")
151 .arg(cursor)
152 .arg("MATCH")
153 .arg(&pattern)
154 .arg("COUNT")
155 .arg(100_u32)
156 .query_async::<(u64, Vec<String>)>(&mut conn)
157 .await;
158 let (next, keys) = match scan {
159 Ok(v) => v,
160 Err(e) => {
161 tracing::warn!("Redis SCAN 失败 pattern={pattern}: {e}");
162 return;
163 }
164 };
165 if !keys.is_empty() {
166 if let Err(e) = redis::cmd("DEL")
167 .arg(keys.as_slice())
168 .query_async::<()>(&mut conn)
169 .await
170 {
171 tracing::warn!("Redis DEL 批量失败 pattern={pattern}: {e}");
172 }
173 }
174 if next == 0 {
175 break;
176 }
177 cursor = next;
178 }
179 }
180
181 pub async fn get_json_bytes(&self, key: &str) -> Option<Arc<Vec<u8>>> {
183 self.get_bytes(&Self::json_cache_key(key))
184 .await
185 .map(Arc::new)
186 }
187
188 pub async fn set_json_bytes_ttl(&self, key: &str, value: &[u8], ttl_secs: u64) {
190 self.set_bytes_ttl(&Self::json_cache_key(key), value, ttl_secs)
191 .await;
192 }
193
194 pub async fn del_json(&self, key: &str) {
196 self.del(&Self::json_cache_key(key)).await;
197 }
198
199 pub async fn del_json_pattern(&self, pattern: &str) {
201 self.del_pattern(&Self::json_cache_key(pattern)).await;
202 }
203
204 pub async fn get_value<T>(&self, namespace: &str, key: &str) -> Option<T>
206 where
207 T: DeserializeOwned,
208 {
209 let logical = Self::value_cache_key(namespace, key);
210 let bytes = self.get_bytes(&logical).await?;
211 match serde_json::from_slice::<T>(&bytes) {
212 Ok(v) => Some(v),
213 Err(e) => {
214 tracing::warn!("Redis JSON 反序列化失败 key={logical}: {e}");
215 None
216 }
217 }
218 }
219
220 pub async fn set_value_ttl<T>(&self, namespace: &str, key: &str, value: &T, ttl_secs: u64)
222 where
223 T: Serialize,
224 {
225 let logical = Self::value_cache_key(namespace, key);
226 let bytes = match serde_json::to_vec(value) {
227 Ok(v) => v,
228 Err(e) => {
229 tracing::warn!("Redis JSON 序列化失败 key={logical}: {e}");
230 return;
231 }
232 };
233 self.set_bytes_ttl(&logical, &bytes, ttl_secs).await;
234 }
235
236 pub async fn del_value(&self, namespace: &str, key: &str) {
238 self.del(&Self::value_cache_key(namespace, key)).await;
239 }
240
241 pub async fn del_value_pattern(&self, namespace: &str, pattern: &str) {
243 self.del_pattern(&Self::value_cache_key(namespace, pattern))
244 .await;
245 }
246
247 pub async fn set_str_ttl(&self, logical: &str, value: &str, ttl_secs: u64) {
249 self.set_bytes_ttl(logical, value.as_bytes(), ttl_secs)
250 .await;
251 }
252
253 pub async fn get_str(&self, logical: &str) -> Option<String> {
255 self.get_bytes(logical)
256 .await
257 .and_then(|b| String::from_utf8(b).ok())
258 }
259
260 pub async fn set_nx_ex(&self, logical: &str, value: &str, ttl_secs: u64) -> bool {
268 let key = self.full_key(logical);
269 let mut conn = self.conn.clone();
270 match redis::cmd("SET")
271 .arg(&key)
272 .arg(value)
273 .arg("NX")
274 .arg("EX")
275 .arg(ttl_secs)
276 .query_async::<Option<String>>(&mut conn)
277 .await
278 {
279 Ok(v) => v.is_some(),
280 Err(e) => {
281 tracing::warn!("Redis SET NX 失败 key={key}: {e}");
282 false
283 }
284 }
285 }
286
287 pub async fn expire(&self, logical: &str, ttl_secs: u64) {
289 let key = self.full_key(logical);
290 let mut conn = self.conn.clone();
291 if let Err(e) = conn.expire::<_, ()>(&key, ttl_secs as i64).await {
292 tracing::warn!("Redis EXPIRE 失败 key={key}: {e}");
293 }
294 }
295
296 pub async fn hset_ttl(&self, logical: &str, field: &str, value: &str, ttl_secs: u64) {
298 let key = self.full_key(logical);
299 let mut conn = self.conn.clone();
300 if let Err(e) = conn.hset::<_, _, _, ()>(&key, field, value).await {
301 tracing::warn!("Redis HSET 失败 key={key}: {e}");
302 return;
303 }
304 if let Err(e) = conn.expire::<_, ()>(&key, ttl_secs as i64).await {
305 tracing::warn!("Redis EXPIRE 失败 key={key}: {e}");
306 }
307 }
308
309 pub async fn hgetall(&self, logical: &str) -> Vec<(String, String)> {
311 let key = self.full_key(logical);
312 let mut conn = self.conn.clone();
313 match conn.hgetall::<_, Vec<(String, String)>>(&key).await {
314 Ok(v) => v,
315 Err(e) => {
316 tracing::warn!("Redis HGETALL 失败 key={key}: {e}");
317 Vec::new()
318 }
319 }
320 }
321
322 pub async fn hdel(&self, logical: &str, field: &str) {
324 let key = self.full_key(logical);
325 let mut conn = self.conn.clone();
326 if let Err(e) = conn.hdel::<_, _, ()>(&key, field).await {
327 tracing::warn!("Redis HDEL 失败 key={key}: {e}");
328 }
329 }
330
331 pub async fn publish(&self, channel: &str, payload: &str) {
333 let ch = self.full_key(channel);
334 let mut conn = self.conn.clone();
335 if let Err(e) = conn.publish::<_, _, ()>(&ch, payload).await {
336 tracing::warn!("Redis PUBLISH 失败 channel={ch}: {e}");
337 }
338 }
339
340 pub async fn subscribe_loop<F>(self, channel: &str, mut handler: F)
348 where
349 F: FnMut(String) + Send + 'static,
350 {
351 let ch = self.full_key(channel);
352 loop {
353 match self.client.get_async_pubsub().await {
354 Ok(mut pubsub) => {
355 if let Err(e) = pubsub.subscribe(&ch).await {
356 tracing::warn!("Redis SUBSCRIBE 失败 channel={ch}: {e}");
357 tokio::time::sleep(std::time::Duration::from_secs(2)).await;
358 continue;
359 }
360 tracing::info!("Redis 订阅频道 {ch} 成功");
361 use futures::StreamExt;
362 let mut stream = pubsub.on_message();
363 while let Some(msg) = stream.next().await {
364 if let Ok(payload) = msg.get_payload::<String>() {
365 handler(payload);
366 }
367 }
368 tracing::warn!("Redis 订阅流中断 channel={ch},准备重连");
369 }
370 Err(e) => {
371 tracing::warn!("Redis 创建 pubsub 连接失败 channel={ch}: {e}");
372 }
373 }
374 tokio::time::sleep(std::time::Duration::from_secs(2)).await;
375 }
376 }
377}