Compare commits

...

2 Commits

Author SHA1 Message Date
Alexey 567453e0f8
Merge pull request #596 from xaosproxy/fix/listen_backlog
feat(server): configurable TCP listen_backlog
2026-03-28 12:28:19 +03:00
sintanial 96ae01078c
feat(server): configurable TCP listen_backlog
Add [server].listen_backlog (default 1024) for client-facing listen(2)
queue size; use the same value for metrics HTTP listeners. Hot reload
logs restart-required when this field changes.
2026-03-27 12:49:53 +03:00
6 changed files with 24 additions and 4 deletions

View File

@ -209,6 +209,10 @@ pub(crate) fn default_server_max_connections() -> u32 {
10_000 10_000
} }
pub(crate) fn default_listen_backlog() -> u32 {
1024
}
pub(crate) fn default_accept_permit_timeout_ms() -> u64 { pub(crate) fn default_accept_permit_timeout_ms() -> u64 {
DEFAULT_ACCEPT_PERMIT_TIMEOUT_MS DEFAULT_ACCEPT_PERMIT_TIMEOUT_MS
} }

View File

@ -570,6 +570,7 @@ fn warn_non_hot_changes(old: &ProxyConfig, new: &ProxyConfig, non_hot_changed: b
} }
if old.server.proxy_protocol != new.server.proxy_protocol if old.server.proxy_protocol != new.server.proxy_protocol
|| !listeners_equal(&old.server.listeners, &new.server.listeners) || !listeners_equal(&old.server.listeners, &new.server.listeners)
|| old.server.listen_backlog != new.server.listen_backlog
|| old.server.listen_addr_ipv4 != new.server.listen_addr_ipv4 || old.server.listen_addr_ipv4 != new.server.listen_addr_ipv4
|| old.server.listen_addr_ipv6 != new.server.listen_addr_ipv6 || old.server.listen_addr_ipv6 != new.server.listen_addr_ipv6
|| old.server.listen_tcp != new.server.listen_tcp || old.server.listen_tcp != new.server.listen_tcp

View File

@ -1272,6 +1272,11 @@ pub struct ServerConfig {
#[serde(default)] #[serde(default)]
pub listeners: Vec<ListenerConfig>, pub listeners: Vec<ListenerConfig>,
/// TCP `listen(2)` backlog for client-facing sockets (also used for the metrics HTTP listener).
/// The effective queue is capped by the kernel (for example `somaxconn` on Linux).
#[serde(default = "default_listen_backlog")]
pub listen_backlog: u32,
/// Maximum number of concurrent client connections. /// Maximum number of concurrent client connections.
/// 0 means unlimited. /// 0 means unlimited.
#[serde(default = "default_server_max_connections")] #[serde(default = "default_server_max_connections")]
@ -1300,6 +1305,7 @@ impl Default for ServerConfig {
metrics_whitelist: default_metrics_whitelist(), metrics_whitelist: default_metrics_whitelist(),
api: ApiConfig::default(), api: ApiConfig::default(),
listeners: Vec::new(), listeners: Vec::new(),
listen_backlog: default_listen_backlog(),
max_connections: default_server_max_connections(), max_connections: default_server_max_connections(),
accept_permit_timeout_ms: default_accept_permit_timeout_ms(), accept_permit_timeout_ms: default_accept_permit_timeout_ms(),
} }

View File

@ -72,6 +72,7 @@ pub(crate) async fn bind_listeners(
let options = ListenOptions { let options = ListenOptions {
reuse_port: listener_conf.reuse_allow, reuse_port: listener_conf.reuse_allow,
ipv6_only: listener_conf.ip.is_ipv6(), ipv6_only: listener_conf.ip.is_ipv6(),
backlog: config.server.listen_backlog,
..Default::default() ..Default::default()
}; };

View File

@ -323,10 +323,12 @@ pub(crate) async fn spawn_metrics_if_configured(
let config_rx_metrics = config_rx.clone(); let config_rx_metrics = config_rx.clone();
let ip_tracker_metrics = ip_tracker.clone(); let ip_tracker_metrics = ip_tracker.clone();
let whitelist = config.server.metrics_whitelist.clone(); let whitelist = config.server.metrics_whitelist.clone();
let listen_backlog = config.server.listen_backlog;
tokio::spawn(async move { tokio::spawn(async move {
metrics::serve( metrics::serve(
port, port,
listen, listen,
listen_backlog,
stats, stats,
beobachten, beobachten,
ip_tracker_metrics, ip_tracker_metrics,

View File

@ -22,6 +22,7 @@ use crate::transport::{ListenOptions, create_listener};
pub async fn serve( pub async fn serve(
port: u16, port: u16,
listen: Option<String>, listen: Option<String>,
listen_backlog: u32,
stats: Arc<Stats>, stats: Arc<Stats>,
beobachten: Arc<BeobachtenStore>, beobachten: Arc<BeobachtenStore>,
ip_tracker: Arc<UserIpTracker>, ip_tracker: Arc<UserIpTracker>,
@ -40,7 +41,7 @@ pub async fn serve(
} }
}; };
let is_ipv6 = addr.is_ipv6(); let is_ipv6 = addr.is_ipv6();
match bind_metrics_listener(addr, is_ipv6) { match bind_metrics_listener(addr, is_ipv6, listen_backlog) {
Ok(listener) => { Ok(listener) => {
info!("Metrics endpoint: http://{}/metrics and /beobachten", addr); info!("Metrics endpoint: http://{}/metrics and /beobachten", addr);
serve_listener( serve_listener(
@ -60,7 +61,7 @@ pub async fn serve(
let mut listener_v6 = None; let mut listener_v6 = None;
let addr_v4 = SocketAddr::from(([0, 0, 0, 0], port)); let addr_v4 = SocketAddr::from(([0, 0, 0, 0], port));
match bind_metrics_listener(addr_v4, false) { match bind_metrics_listener(addr_v4, false, listen_backlog) {
Ok(listener) => { Ok(listener) => {
info!( info!(
"Metrics endpoint: http://{}/metrics and /beobachten", "Metrics endpoint: http://{}/metrics and /beobachten",
@ -74,7 +75,7 @@ pub async fn serve(
} }
let addr_v6 = SocketAddr::from(([0, 0, 0, 0, 0, 0, 0, 0], port)); let addr_v6 = SocketAddr::from(([0, 0, 0, 0, 0, 0, 0, 0], port));
match bind_metrics_listener(addr_v6, true) { match bind_metrics_listener(addr_v6, true, listen_backlog) {
Ok(listener) => { Ok(listener) => {
info!( info!(
"Metrics endpoint: http://[::]:{}/metrics and /beobachten", "Metrics endpoint: http://[::]:{}/metrics and /beobachten",
@ -122,10 +123,15 @@ pub async fn serve(
} }
} }
fn bind_metrics_listener(addr: SocketAddr, ipv6_only: bool) -> std::io::Result<TcpListener> { fn bind_metrics_listener(
addr: SocketAddr,
ipv6_only: bool,
listen_backlog: u32,
) -> std::io::Result<TcpListener> {
let options = ListenOptions { let options = ListenOptions {
reuse_port: false, reuse_port: false,
ipv6_only, ipv6_only,
backlog: listen_backlog,
..Default::default() ..Default::default()
}; };
let socket = create_listener(addr, &options)?; let socket = create_listener(addr, &options)?;