From b67e4b46b4b98ef10a6613d34c50f5bdd1e79b17 Mon Sep 17 00:00:00 2001 From: SurName <472256532@qq.com> Date: Wed, 27 Nov 2024 09:53:16 +0800 Subject: [PATCH] Allowing dns cache to be shared across multiple client --- .../src/async_impl/dns/resolver.rs | 149 ++++++++++++++---- 1 file changed, 120 insertions(+), 29 deletions(-) diff --git a/ylong_http_client/src/async_impl/dns/resolver.rs b/ylong_http_client/src/async_impl/dns/resolver.rs index ab021bb..a62c4bb 100644 --- a/ylong_http_client/src/async_impl/dns/resolver.rs +++ b/ylong_http_client/src/async_impl/dns/resolver.rs @@ -19,7 +19,7 @@ use std::io; use std::io::Error; use std::net::{SocketAddr, ToSocketAddrs}; use std::pin::Pin; -use std::sync::{Arc, Mutex}; +use std::sync::{Arc, Mutex, Once}; use std::task::{Context, Poll}; use std::time::{Duration, Instant}; use std::vec::IntoIter; @@ -97,7 +97,6 @@ impl Future for DefaultDnsFuture { /// Default dns resolver used by the `Client`. /// DefaultDnsResolver provides DNS resolver with caching machanism. pub struct DefaultDnsResolver { - manager: DnsManager, // Manages DNS cache connector: DnsConnector, // Performing DNS resolution ttl: Duration, // Time-to-live for the DNS cache } @@ -107,7 +106,6 @@ impl Default for DefaultDnsResolver { // seconds. fn default() -> Self { DefaultDnsResolver { - manager: DnsManager::default(), connector: DnsConnector {}, ttl: DEFAULT_TTL, // Default TTL set to 60 seconds } @@ -128,24 +126,26 @@ impl DefaultDnsResolver { /// ``` pub fn new(ttl: Duration) -> Self { DefaultDnsResolver { - manager: DnsManager::new(), connector: DnsConnector {}, ttl, // Set TTL through the passed parameters } } } -#[derive(Default)] +static mut DNS_MANAGER: Option = None; +static DNS_MANAGER_INIT: Once = Once::new(); + +#[derive(Clone)] struct DnsManager { // Cache storing authority and DNS results - map: Mutex>, + map: Arc>>, } impl DnsManager { // Creates a new `DnsManager` instance with an empty cache fn new() -> Self { DnsManager { - map: Mutex::new(HashMap::new()), + map: Arc::new(Mutex::new(HashMap::new())), } } @@ -156,6 +156,15 @@ impl DnsManager { map_lock.retain(|_, result| result.inner.lock().unwrap().is_valid()); } } + + fn get_dns_manager() -> Arc> { + unsafe { + DNS_MANAGER_INIT.call_once(|| { + DNS_MANAGER = Some(DnsManager::new()); + }); + Arc::new(Mutex::new(DNS_MANAGER.as_ref().unwrap().clone())) + } + } } struct DnsResult { @@ -213,9 +222,11 @@ impl DnsConnector { impl Resolver for DefaultDnsResolver { fn resolve(&self, authority: &str) -> SocketFuture { let authority = authority.to_string(); - self.manager.clean_expired_entries(); Box::pin(async move { - let mut map_lock = self.manager.map.lock().unwrap(); + let get_dns_manager = DnsManager::get_dns_manager(); + let manager_lock = get_dns_manager.lock().unwrap(); + manager_lock.clean_expired_entries(); + let mut map_lock = manager_lock.map.lock().unwrap(); if let Some(addrs) = map_lock.get(&authority) { let lock_inner = addrs.inner.lock().unwrap(); if lock_inner.is_valid() { @@ -244,7 +255,8 @@ mod ut_dns_cache { use super::*; - /// UT test cases for `DefaultDnsResolver::resolve`. + /// UT test cases for `DefaultDnsResolver::resolve` with single thread and + /// single resolver. /// /// # Brief /// 1. Test that DNS resolution is correctly cached after the first @@ -254,14 +266,13 @@ mod ut_dns_cache { /// 3. Check that after TTL expiration, the resolver performs another /// resolution and returns fresh results and is slower that second one. #[tokio::test] - async fn ut_dns_cache_test_async() { - let resolver = DefaultDnsResolver::new(Duration::from_millis(100)); + async fn ut_dns_cache_single_thread_single_resolver_async() { + let resolver = DefaultDnsResolver::new(Duration::from_millis(50)); let domain = "example.com:0"; let start = Instant::now(); let addrs1 = resolver.resolve(domain).await; let duration1 = start.elapsed(); assert!(addrs1.is_ok()); - tokio::time::sleep(Duration::from_millis(80)).await; let start = Instant::now(); let addrs2 = resolver.resolve(domain).await; let duration2 = start.elapsed(); @@ -272,15 +283,15 @@ mod ut_dns_cache { assert_eq!(addrs1_vec, addrs2_vec); } assert!(duration1 > duration2); - tokio::time::sleep(Duration::from_millis(80)).await; + tokio::time::sleep(Duration::from_millis(60)).await; let start = Instant::now(); let _addrs3 = resolver.resolve(domain).await; let duration3 = start.elapsed(); assert!(duration3 > duration2); } - /// UT test cases for `DefaultDnsResolver::resolve` under multiple - /// concurrent requests. + /// UT test cases for `DefaultDnsResolver::resolve` with multiple + /// concurrent requests and single resolver. /// /// # Brief /// 1. Test that multiple concurrent DNS resolution requests return the same @@ -290,7 +301,7 @@ mod ut_dns_cache { /// 3. Check that subsequent requests are faster, as they use the cached /// result. #[tokio::test] - async fn ut_dns_cache_test_multi_async() { + async fn ut_dns_cache_multi_thread_single_resolver_async() { let domain = "example.com:0"; let resolver = Arc::new(Mutex::new(DefaultDnsResolver::new(Duration::from_millis( 500, @@ -328,6 +339,44 @@ mod ut_dns_cache { handle.await.unwrap(); } } + + /// UT test cases for `DefaultDnsResolver::resolve` with multiple resolvers. + /// + /// # Brief + /// 1. Verify that all resolution use the same dns cache. + /// 1. Test that the first DNS resolution is correctly cached after the + /// first resolution. + /// 2. Ensure that the second resolution within TTL returns the same result + /// and is faster. + /// 3. Check that after TTL expiration, the third resolver returns fresh + /// results and is slower that second one. + #[tokio::test] + async fn ut_dns_cache_multi_resolver_async() { + let domain = "example.com:0"; + let resolver1 = DefaultDnsResolver::new(Duration::from_millis(100)); + let start = Instant::now(); + let addrs1 = resolver1.resolve(domain).await; + let duration1 = start.elapsed(); + assert!(addrs1.is_ok()); + tokio::time::sleep(Duration::from_millis(50)).await; + let resolver2 = DefaultDnsResolver::new(Duration::from_millis(200)); + let start = Instant::now(); + let addrs2 = resolver2.resolve(domain).await; + let duration2 = start.elapsed(); + assert!(addrs2.is_ok()); + if let (Ok(addrs1), Ok(addrs2)) = (addrs1, addrs2) { + let addrs1_vec: Vec = addrs1.collect(); + let addrs2_vec: Vec = addrs2.collect(); + assert_eq!(addrs1_vec, addrs2_vec); + } + assert!(duration1 > duration2); + tokio::time::sleep(Duration::from_millis(80)).await; + let resolver3 = DefaultDnsResolver::new(Duration::from_millis(300)); + let start = Instant::now(); + let _addrs3 = resolver3.resolve(domain).await; + let duration3 = start.elapsed(); + assert!(duration3 > duration2); + } } #[cfg(feature = "ylong_base")] @@ -340,7 +389,8 @@ mod ut_dns_cache { use super::*; - /// UT test cases for `DefaultDnsResolver::resolve`. + /// UT test cases for `DefaultDnsResolver::resolve` with single thread and + /// single resolver. /// /// # Brief /// 1. Test that DNS resolution is correctly cached after the first @@ -350,18 +400,17 @@ mod ut_dns_cache { /// 3. Check that after TTL expiration, the resolver performs another /// resolution and returns fresh results and is slower that second one. #[test] - fn ut_dns_cache_test() { - ylong_runtime::block_on(ut_dns_cache_test_async()); + fn ut_dns_cache_single_thread_single_resolver() { + ylong_runtime::block_on(ut_dns_cache_single_thread_single_resolver_async()); } - async fn ut_dns_cache_test_async() { - let resolver = DefaultDnsResolver::new(Duration::from_millis(100)); + async fn ut_dns_cache_single_thread_single_resolver_async() { + let resolver = DefaultDnsResolver::new(Duration::from_millis(50)); let domain = "example.com:0"; let start = Instant::now(); let addrs1 = resolver.resolve(domain).await; let duration1 = start.elapsed(); assert!(addrs1.is_ok()); - ylong_runtime::time::sleep(Duration::from_millis(80)).await; let start = Instant::now(); let addrs2 = resolver.resolve(domain).await; let duration2 = start.elapsed(); @@ -372,15 +421,15 @@ mod ut_dns_cache { assert_eq!(addrs1_vec, addrs2_vec); } assert!(duration1 > duration2); - ylong_runtime::time::sleep(Duration::from_millis(80)).await; + ylong_runtime::time::sleep(Duration::from_millis(60)).await; let start = Instant::now(); let _addrs3 = resolver.resolve(domain).await; let duration3 = start.elapsed(); assert!(duration3 > duration2); } - /// UT test cases for `DefaultDnsResolver::resolve` under multiple - /// concurrent requests. + /// UT test cases for `DefaultDnsResolver::resolve` with multiple + /// concurrent requests and single resolver. /// /// # Brief /// 1. Test that multiple concurrent DNS resolution requests return the same @@ -390,11 +439,11 @@ mod ut_dns_cache { /// 3. Check that subsequent requests are faster, as they use the cached /// result. #[test] - fn ut_dns_cache_test_multi() { - ylong_runtime::block_on(ut_dns_cache_test_multi_async()); + fn ut_dns_cache_multi_thread_single_resolver() { + ylong_runtime::block_on(ut_dns_cache_multi_thread_single_resolver_async()); } - async fn ut_dns_cache_test_multi_async() { + async fn ut_dns_cache_multi_thread_single_resolver_async() { let domain = "example.com:0"; let resolver = Arc::new(Mutex::new(DefaultDnsResolver::new(Duration::from_millis( 500, @@ -431,4 +480,46 @@ mod ut_dns_cache { handle.await.unwrap(); } } + + /// UT test cases for `DefaultDnsResolver::resolve` with multiple resolvers. + /// + /// # Brief + /// 1. Verify that all resolution use the same dns cache. + /// 1. Test that the first DNS resolution is correctly cached after the + /// first resolution. + /// 2. Ensure that the second resolution within TTL returns the same result + /// and is faster. + /// 3. Check that after TTL expiration, the third resolver returns fresh + /// results and is slower that second one. + #[test] + fn ut_dns_cache_multi_resolver() { + ylong_runtime::block_on(ut_dns_cache_multi_resolver_async()); + } + + async fn ut_dns_cache_multi_resolver_async() { + let domain = "example.com:0"; + let resolver1 = DefaultDnsResolver::new(Duration::from_millis(100)); + let start = Instant::now(); + let addrs1 = resolver1.resolve(domain).await; + let duration1 = start.elapsed(); + assert!(addrs1.is_ok()); + ylong_runtime::time::sleep(Duration::from_millis(50)).await; + let resolver2 = DefaultDnsResolver::new(Duration::from_millis(200)); + let start = Instant::now(); + let addrs2 = resolver2.resolve(domain).await; + let duration2 = start.elapsed(); + assert!(addrs2.is_ok()); + if let (Ok(addrs1), Ok(addrs2)) = (addrs1, addrs2) { + let addrs1_vec: Vec = addrs1.collect(); + let addrs2_vec: Vec = addrs2.collect(); + assert_eq!(addrs1_vec, addrs2_vec); + } + assert!(duration1 > duration2); + ylong_runtime::time::sleep(Duration::from_millis(80)).await; + let resolver3 = DefaultDnsResolver::new(Duration::from_millis(300)); + let start = Instant::now(); + let _addrs3 = resolver3.resolve(domain).await; + let duration3 = start.elapsed(); + assert!(duration3 > duration2); + } } -- Gitee