mirror of
https://github.com/DefectingCat/candy
synced 2025-07-15 08:41:35 +00:00
perf(hashmap): use dashmap as global store
This commit is contained in:
30
Cargo.lock
generated
30
Cargo.lock
generated
@ -345,6 +345,7 @@ dependencies = [
|
||||
"bytes",
|
||||
"clap",
|
||||
"const_format",
|
||||
"dashmap",
|
||||
"futures-util",
|
||||
"http",
|
||||
"http-body-util",
|
||||
@ -498,6 +499,12 @@ dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
|
||||
|
||||
[[package]]
|
||||
name = "crypto-common"
|
||||
version = "0.1.6"
|
||||
@ -508,6 +515,21 @@ dependencies = [
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dashmap"
|
||||
version = "6.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"crossbeam-utils",
|
||||
"hashbrown 0.14.5",
|
||||
"lock_api",
|
||||
"once_cell",
|
||||
"parking_lot_core",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.10.7"
|
||||
@ -699,6 +721,12 @@ dependencies = [
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.14.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.15.2"
|
||||
@ -854,7 +882,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown",
|
||||
"hashbrown 0.15.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -30,6 +30,7 @@ toml = "0.8.20"
|
||||
bytes = "1.10.1"
|
||||
const_format = "0.2.34"
|
||||
md5 = "0.7.0"
|
||||
dashmap = { version = "6.1.0", features = ["serde"] }
|
||||
# logging
|
||||
tracing = "0.1.41"
|
||||
tracing-subscriber = { version = "0.3.19", features = ["env-filter"] }
|
||||
|
@ -5,9 +5,10 @@ use crate::{
|
||||
},
|
||||
error::Result,
|
||||
};
|
||||
use std::{borrow::Cow, collections::BTreeMap, fs};
|
||||
use std::{borrow::Cow, fs};
|
||||
|
||||
use anyhow::Context;
|
||||
use dashmap::DashMap;
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Deserialize, Clone, Debug)]
|
||||
@ -42,9 +43,9 @@ pub struct SettingRoute {
|
||||
|
||||
/// Host routes
|
||||
/// Each host can have multiple routes
|
||||
pub type HostRouteMap = BTreeMap<String, SettingRoute>;
|
||||
pub type HostRouteMap = DashMap<String, SettingRoute>;
|
||||
/// headers
|
||||
pub type HeaderMap = BTreeMap<String, String>;
|
||||
pub type HeaderMap = DashMap<String, String>;
|
||||
|
||||
/// Virtual host
|
||||
/// Each host can listen on one port and one ip
|
||||
@ -63,12 +64,12 @@ pub struct SettingHost {
|
||||
pub certificate_key: Option<String>,
|
||||
/// Routes in config file
|
||||
pub route: Vec<SettingRoute>,
|
||||
/// Host routes convert from Vec<SettingRoute> to BTreeMap<String, SettingRoute>
|
||||
/// Host routes convert from Vec<SettingRoute> to DashMap<String, SettingRoute>
|
||||
/// {
|
||||
/// "/doc": <SettingRoute>
|
||||
/// }
|
||||
#[serde(skip)]
|
||||
pub route_map: BTreeMap<String, SettingRoute>,
|
||||
pub route_map: DashMap<String, SettingRoute>,
|
||||
/// HTTP keep-alive timeout
|
||||
#[serde(default = "timeout_default")]
|
||||
pub timeout: u16,
|
||||
@ -77,7 +78,7 @@ pub struct SettingHost {
|
||||
pub headers: Option<HeaderMap>,
|
||||
}
|
||||
|
||||
pub type MIMEType = BTreeMap<Cow<'static, str>, Cow<'static, str>>;
|
||||
pub type MIMEType = DashMap<Cow<'static, str>, Cow<'static, str>>;
|
||||
|
||||
/// Whole config settings
|
||||
#[derive(Deserialize, Clone, Debug, Default)]
|
||||
|
@ -1,4 +1,6 @@
|
||||
use std::{borrow::Cow, collections::BTreeMap, env};
|
||||
use std::{borrow::Cow, env};
|
||||
|
||||
use dashmap::DashMap;
|
||||
|
||||
use crate::config::MIMEType;
|
||||
|
||||
@ -36,7 +38,7 @@ pub fn upstream_timeout_default() -> u16 {
|
||||
|
||||
// default mime types
|
||||
pub fn types_default() -> MIMEType {
|
||||
BTreeMap::new()
|
||||
DashMap::new()
|
||||
}
|
||||
// macro_rules! insert_mime {
|
||||
// ($name:literal, $mime:ident, $map:ident) => {
|
||||
|
@ -1,5 +1,4 @@
|
||||
use std::{
|
||||
collections::BTreeMap,
|
||||
path::Path,
|
||||
sync::{Arc, LazyLock},
|
||||
time::Duration,
|
||||
@ -7,10 +6,11 @@ use std::{
|
||||
|
||||
use anyhow::anyhow;
|
||||
use axum::{Router, extract::Request, middleware, routing::get};
|
||||
use dashmap::DashMap;
|
||||
use futures_util::pin_mut;
|
||||
use hyper::body::Incoming;
|
||||
use hyper_util::rt::{TokioExecutor, TokioIo};
|
||||
use tokio::{net::TcpListener, sync::RwLock};
|
||||
use tokio::net::TcpListener;
|
||||
use tokio_rustls::{
|
||||
TlsAcceptor,
|
||||
rustls::{
|
||||
@ -42,14 +42,13 @@ pub mod serve;
|
||||
/// "/doc": <SettingRoute>
|
||||
/// }
|
||||
/// }
|
||||
pub static HOSTS: LazyLock<RwLock<BTreeMap<u16, SettingHost>>> =
|
||||
LazyLock::new(|| RwLock::new(BTreeMap::new()));
|
||||
pub static HOSTS: LazyLock<DashMap<u16, SettingHost>> = LazyLock::new(DashMap::new);
|
||||
|
||||
// static ROUTE_MAP: LazyLock<RwLock<HostRouteMap>> = LazyLock::new(|| RwLock::new(BTreeMap::new()));
|
||||
|
||||
pub async fn make_server(host: SettingHost) -> anyhow::Result<()> {
|
||||
let mut router = Router::new();
|
||||
let mut host_to_save = host.clone();
|
||||
let host_to_save = host.clone();
|
||||
// find routes in config
|
||||
// convert to axum routes
|
||||
// register routes
|
||||
@ -114,7 +113,7 @@ pub async fn make_server(host: SettingHost) -> anyhow::Result<()> {
|
||||
}
|
||||
|
||||
// save host to map
|
||||
HOSTS.write().await.insert(host.port, host_to_save);
|
||||
HOSTS.insert(host.port, host_to_save);
|
||||
|
||||
router = router.layer(
|
||||
ServiceBuilder::new()
|
||||
|
@ -6,6 +6,7 @@ use axum::{
|
||||
response::{IntoResponse, Response},
|
||||
};
|
||||
use axum_extra::extract::Host;
|
||||
use dashmap::mapref::one::Ref;
|
||||
use futures_util::StreamExt;
|
||||
use http::{
|
||||
HeaderValue, StatusCode, Uri,
|
||||
@ -129,9 +130,8 @@ pub async fn serve(
|
||||
// which is `host_route.location`
|
||||
let scheme = request.uri().scheme_str().unwrap_or("http");
|
||||
let port = parse_port_from_host(&host, scheme).ok_or(RouteError::BadRequest())?;
|
||||
let hosts = &HOSTS.read().await;
|
||||
let route_map = &hosts.get(&port).ok_or(RouteError::BadRequest())?.route_map;
|
||||
debug!("Route map entries: {:?}", route_map.keys());
|
||||
let route_map = &HOSTS.get(&port).ok_or(RouteError::BadRequest())?.route_map;
|
||||
debug!("Route map entries: {:?}", route_map);
|
||||
let host_route = route_map
|
||||
.get(&parent_path)
|
||||
.ok_or(RouteError::RouteNotFound())?;
|
||||
@ -155,10 +155,10 @@ pub async fn serve(
|
||||
if path.contains('.') {
|
||||
vec![format!("{}/{}", root, path)]
|
||||
} else {
|
||||
generate_default_index(host_route, &format!("{root}/{path}"))
|
||||
generate_default_index(&host_route, &format!("{root}/{path}"))
|
||||
}
|
||||
} else {
|
||||
generate_default_index(host_route, root)
|
||||
generate_default_index(&host_route, root)
|
||||
};
|
||||
debug!("request index file {:?}", path_arr);
|
||||
// Try each candidate path in order:
|
||||
@ -193,7 +193,7 @@ pub async fn serve(
|
||||
/// ## Arguments
|
||||
/// - `host_route`: the host route config
|
||||
/// - `root`: the root path
|
||||
fn generate_default_index(host_route: &SettingRoute, root: &str) -> Vec<String> {
|
||||
fn generate_default_index(host_route: &Ref<'_, String, SettingRoute>, root: &str) -> Vec<String> {
|
||||
let indices = if host_route.index.is_empty() {
|
||||
let host_iter = HOST_INDEX
|
||||
.iter()
|
||||
|
@ -94,24 +94,25 @@ pub async fn add_headers(Host(host): Host, req: Request, next: Next) -> impl Int
|
||||
debug!("port {:?}", port);
|
||||
let mut res = next.run(req).await;
|
||||
let req_headers = res.headers_mut();
|
||||
let host = HOSTS.read().await;
|
||||
let Some(host) = host.get(&port) else {
|
||||
// let host = HOSTS.read().await;
|
||||
let Some(host) = HOSTS.get(&port) else {
|
||||
return res;
|
||||
};
|
||||
let Some(headers) = host.headers.as_ref() else {
|
||||
return res;
|
||||
};
|
||||
for (key, value) in headers {
|
||||
headers.iter().for_each(|entery| {
|
||||
let (key, value) = (entery.key(), entery.value());
|
||||
let Ok(header_name) = HeaderName::from_bytes(key.as_bytes()) else {
|
||||
error!("Invalid header name: {key}");
|
||||
break;
|
||||
return;
|
||||
};
|
||||
let Ok(header_value) = HeaderValue::from_bytes(value.as_bytes()) else {
|
||||
error!("Invalid header value: {value}");
|
||||
break;
|
||||
return;
|
||||
};
|
||||
req_headers.append(header_name, header_value);
|
||||
}
|
||||
});
|
||||
res
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user