Rust在寫庫時實現(xiàn)緩存的操作方法
Rust在寫庫時實現(xiàn)緩存
依賴
在寫庫時,實現(xiàn)一個緩存請求,需要用到全局變量,所以我們可以添加cratelazy_static
Cargo.toml添加以下依賴
[dependencies] chrono = "0.4.31" lazy_static = "1.4.0" reqwest = { version = "0.11.23", features = ["blocking", "json"] } serde = { version = "1.0.193", features = ["derive"] } serde_json = "1.0.108"
代碼實現(xiàn)
use std::{sync::Mutex, collections::HashMap}; use chrono::{DateTime, Utc}; use lazy_static::lazy_static; use serde_json::Value; lazy_static! { static ref REQUESTS_RESPONSE_CACHE: Mutex<HashMap<String, RequestsResponseCache>> = Mutex::new(HashMap::new()); } pub struct RequestsResponseCache { pub response: Value, pub datetime: DateTime<Utc>, } pub fn get_requests_response_cache(url: &str) -> Result<Value, reqwest::Error> { let mut cache = REQUESTS_RESPONSE_CACHE.lock().unwrap(); if let Some(cache_entry) = cache.get(url) { let elapsed = Utc::now() - cache_entry.datetime; if elapsed.num_seconds() > 3600 { let response: Value = reqwest::blocking::get(url)?.json()?; let res = response.clone(); let cache_entry = RequestsResponseCache { response, datetime: Utc::now(), }; cache.insert(url.to_string(), cache_entry); return Ok(res); } } let response: Value = reqwest::blocking::get(url)?.json()?; let res = response.clone(); let cache_entry = RequestsResponseCache { response, datetime: Utc::now(), }; cache.insert(url.to_string(), cache_entry); Ok(res) }
使用了 lazy_static
宏創(chuàng)建了一個靜態(tài)的全局變量 REQUESTS_RESPONSE_CACHE
,這個全局變量是一個 Mutex
包裹的 HashMap
,用來存儲請求的響應(yīng)緩存。這個緩存是線程安全的,因為被 Mutex
包裹了起來,這樣就可以在多個線程中安全地訪問和修改這個緩存。
接著定義了一個 RequestsResponseCache
結(jié)構(gòu)體,用來表示緩存中的一個條目,其中包含了響應(yīng)數(shù)據(jù) response
和緩存的時間戳 datetime
。
然后定義了一個 get_requests_response_cache
函數(shù),用來從緩存中獲取請求的響應(yīng)。它首先嘗試從緩存中獲取指定 url
的響應(yīng)數(shù)據(jù),如果緩存中有對應(yīng)的條目,并且距離上次緩存的時間超過了 3600 秒(1 小時),則重新發(fā)起請求并更新緩存,然后返回響應(yīng)數(shù)據(jù)。如果緩存中沒有對應(yīng)的條目,或者緩存的時間未超過 3600 秒,則直接發(fā)起請求并更新緩存,然后返回響應(yīng)數(shù)據(jù)。
這樣就提供了一個簡單的請求響應(yīng)的緩存功能,能夠在需要時緩存請求的響應(yīng)數(shù)據(jù),并在一定時間內(nèi)有效,從而減少對遠(yuǎn)程服務(wù)的重復(fù)請求,提高程序性能。
補(bǔ)充:
rust緩存庫moka簡介
關(guān)于moka
“Moka” 是一個用于 Rust 的高性能緩存庫,它提供了多種類型的緩存數(shù)據(jù)結(jié)構(gòu),包括哈希表、LRU(最近最少使用)緩存和 支持TTL(生存時間)緩存。
以下是一些 “moka” 庫的特點(diǎn)和功能:
- 多種緩存類型: “moka” 提供了多種緩存類型,包括哈希表緩存、LRU 緩存和 TTL 緩存。你可以根據(jù)具體的需求選擇適合的緩存類型。
- 線程安全: “moka” 庫是線程安全的,可以在多線程環(huán)境中使用,不需要額外的同步措施。
- 高性能: “moka” 的設(shè)計目標(biāo)之一是提供高性能的緩存實現(xiàn)。它經(jīng)過優(yōu)化,能夠在高并發(fā)場景下快速處理緩存操作。
- 可配置性: “moka” 允許你根據(jù)需要對緩存進(jìn)行配置,如容量限制、緩存項的最大生存時間等。
moka的github地址:moka。
moka的使用示例
1.事件通知:
支持在緩存項發(fā)生過期淘汰、用戶主動淘汰、緩存池大小受限強(qiáng)制淘汰時,觸發(fā)回調(diào)函數(shù)執(zhí)行一些后續(xù)任務(wù)。
use moka::{notification::RemovalCause, sync::Cache}; use std::time::{Duration,Instant}; fn main() { // 創(chuàng)建一個緩存項事件監(jiān)聽閉包 let now = Instant::now(); let listener = move |k, v: String, cause| { // 監(jiān)聽緩存項的觸發(fā)事件,RemovalCause包含四種場景:Expired(緩存項過期)、Explicit(用戶主動移除緩存)、Replaced(緩存項發(fā)生更新或替換)、Size(緩存數(shù)量達(dá)到上限驅(qū)逐)。 println!( "== An entry has been evicted. time:{} k: {:?}, v: {:?},cause:{:?}", now.elapsed().as_secs(), k, v, cause ); // 針對不同事項,進(jìn)行處理。 // match cause { // RemovalCause::Expired => {} // RemovalCause::Explicit => {} // RemovalCause::Replaced => {} // RemovalCause::Size => {} // } }; //緩存生存時間:10s let ttl_time = Duration::from_secs(10); // 創(chuàng)建一個具有過期時間和淘汰機(jī)制的緩存 let cache: Cache<String, String> = Cache::builder() .time_to_idle(ttl_time) .eviction_listener(listener) .build(); // insert 緩存項 cache.insert("key1".to_string(), "value1".to_string()); cache.insert("key2".to_string(), "value2".to_string()); cache.insert("key3".to_string(), "value3".to_string()); // 5s后使用key1 std::thread::sleep(Duration::from_secs(5)); if let Some(value) = cache.get(&"key1".to_string()) { println!("5s: Value of key1: {}", value); } cache.remove("key3"); println!("5s: remove key3"); // 等待 6 秒,讓緩存項key2過期 std::thread::sleep(Duration::from_secs(6)); // 嘗試獲取緩存項 "key1" 的值 if let Some(value) = cache.get("key1") { println!("11s: Value of key1: {}", value); } else { println!("Key1 has expired."); } // 嘗試獲取緩存項 "key2" 的值 if let Some(value) = cache.get("key2") { println!("11s: Value of key2: {}", value); } else { println!("Key2 has expired."); } // 嘗試獲取緩存項 "key3" 的值 if let Some(value) = cache.get("key3") { println!("11s: Value of key3: {}", value); } else { println!("Key3 has removed."); } // 空置9s后 std::thread::sleep(Duration::from_secs(11)); // 再次嘗試獲取緩存項 "key1" 的值 if let Some(value) = cache.get("key1") { println!("22s: Value of key1: {}", value); } else { println!("Key1 has expired."); } }
運(yùn)行結(jié)果:
5s: Value of key1: value1
== An entry has been evicted. time:5 k: "key3", v: "value3",cause:Explicit
5s: remove key3
== An entry has been evicted. time:10 k: "key2", v: "value2",cause:Expired
11s: Value of key1: value1
Key2 has expired.
Key3 has removed.
== An entry has been evicted. time:21 k: "key1", v: "value1",cause:Expired
Key1 has expired.
2.支持同步并發(fā):
use moka::sync::Cache; use std::thread; fn value(n: usize) -> String { format!("value {}", n) } fn main() { const NUM_THREADS: usize = 3; const NUM_KEYS_PER_THREAD: usize = 2; // Create a cache that can store up to 6 entries. let cache = Cache::new(6); // Spawn threads and read and update the cache simultaneously. let threads: Vec<_> = (0..NUM_THREADS) .map(|i| { // To share the same cache across the threads, clone it. // This is a cheap operation. let my_cache = cache.clone(); let start = i * NUM_KEYS_PER_THREAD; let end = (i + 1) * NUM_KEYS_PER_THREAD; thread::spawn(move || { // Insert 2 entries. (NUM_KEYS_PER_THREAD = 2) for key in start..end { my_cache.insert(key, value(key)); println!("{}",my_cache.get(&key).unwrap()); } // Invalidate every 2 element of the inserted entries. for key in (start..end).step_by(2) { my_cache.invalidate(&key); } }) }) .collect(); // Wait for all threads to complete. threads.into_iter().for_each(|t| t.join().expect("Failed")); // Verify the result. for key in 0..(NUM_THREADS * NUM_KEYS_PER_THREAD) { if key % 2 == 0 { assert_eq!(cache.get(&key), None); } else { assert_eq!(cache.get(&key), Some(value(key))); } } }
結(jié)果:
value 2
value 3
value 0
value 4
value 1
value 5
并發(fā)讀寫cahce中的數(shù)據(jù)不會產(chǎn)生異常。
3.下面是moka庫example中給出的異步示例:
use moka::future::Cache; #[tokio::main] async fn main() { const NUM_TASKS: usize = 16; const NUM_KEYS_PER_TASK: usize = 64; fn value(n: usize) -> String { format!("value {}", n) } // Create a cache that can store up to 10,000 entries. let cache = Cache::new(10_000); // Spawn async tasks and write to and read from the cache. let tasks: Vec<_> = (0..NUM_TASKS) .map(|i| { // To share the same cache across the async tasks, clone it. // This is a cheap operation. let my_cache = cache.clone(); let start = i * NUM_KEYS_PER_TASK; let end = (i + 1) * NUM_KEYS_PER_TASK; tokio::spawn(async move { // Insert 64 entries. (NUM_KEYS_PER_TASK = 64) for key in start..end { // insert() is an async method, so await it. my_cache.insert(key, value(key)).await; // get() returns Option<String>, a clone of the stored value. assert_eq!(my_cache.get(&key), Some(value(key))); } // Invalidate every 4 element of the inserted entries. for key in (start..end).step_by(4) { // invalidate() is an async method, so await it. my_cache.invalidate(&key).await; } }) }) .collect(); // Wait for all tasks to complete. futures_util::future::join_all(tasks).await; // Verify the result. for key in 0..(NUM_TASKS * NUM_KEYS_PER_TASK) { if key % 4 == 0 { assert_eq!(cache.get(&key), None); } else { assert_eq!(cache.get(&key), Some(value(key))); } } }
到此這篇關(guān)于Rust在寫庫時實現(xiàn)緩存的文章就介紹到這了,更多相關(guān)Rust緩存內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Rust循環(huán)控制結(jié)構(gòu)用法詳解
Rust提供了多種形式的循環(huán)結(jié)構(gòu),每種都適用于不同的場景,在Rust中,循環(huán)有三種主要的形式:loop、while和for,本文將介紹Rust中的這三種循環(huán),并通過實例展示它們的用法和靈活性,感興趣的朋友一起看看吧2024-02-02Rust初體驗:手把手教你構(gòu)建‘Hello,?World!’
"準(zhǔn)備好了嗎?一起踏上Rust編程語言的精彩旅程!在這篇「Rust初體驗」中,我們將手把手教你構(gòu)建經(jīng)典程序“Hello,?World!”,感受Rust的強(qiáng)大與安全,短短幾行代碼,就能讓你對這個系統(tǒng)級語言的魅力一探究竟!快加入吧,驚喜等你發(fā)現(xiàn)!"2024-01-01Rust開發(fā)WebAssembly在Html和Vue中的應(yīng)用小結(jié)(推薦)
這篇文章主要介紹了Rust開發(fā)WebAssembly在Html和Vue中的應(yīng)用,本文將帶領(lǐng)大家在普通html上和vue手腳架上都來運(yùn)行wasm的流程,需要的朋友可以參考下2022-08-08