Introduction and Background
In the previous section, we implemented an HTTPS server that supports public network access, HTTP/3, HTTP/2, and multiple domains, initially integrating high-performance features. This article will focus on further enhancing the high-performance optimization aspects, covering TLS optimization, QUIC optimization, concurrency optimization, network optimization, certificate management, monitoring and tuning, and load balancing, to build a complete, comprehensive, and ultra-high-performance service. The goal is to create an HTTPS server that can stably operate in high concurrency and complex public network environments while supporting multiple domains (e.g., a.example.com
, b.example.com
) and the latest protocols (HTTP/3 and HTTP/2).
We will leverage the hyper
, rustls
, and quinn
ecosystem in Rust to deeply optimize each aspect, providing detailed theoretical analysis and improved code examples to ensure the server meets production-level standards in performance, reliability, and scalability.
1. Optimization Goals and Theoretical Deepening
Based on the high-performance optimization section from the previous chapter, we further refine our goals and supplement the theory:
1.1 TLS Optimization
- Enforce TLS 1.3: Disable TLS 1.2 and only support the latest protocol to simplify the handshake and enhance security.
- 0-RTT Enhancement: Optimize early data handling to reduce initial connection latency while preventing replay attacks.
- Session Resumption: Use dynamic session tickets to support quick recovery across connections.
- Encryption Algorithms: Prefer hardware-accelerated algorithms (e.g., AES-GCM) and provide fallbacks (e.g., ChaCha20).
- Certificate Compression: Enable TLS certificate compression (e.g., Brotli or Zlib) to reduce handshake overhead.
1.2 QUIC Optimization
- Datagram Optimization: Dynamically adjust the maximum datagram size to adapt to different network MTUs.
- Congestion Control: Implement adaptive algorithms (e.g., BBR) to optimize throughput and latency.
- Connection Migration: Enhance QUIC connection ID management to support network switching for mobile devices.
- Stream Management: Limit the maximum concurrent streams to prevent a single client from exhausting resources.
- Multipath QUIC: Experimental support for multipath transmission to fully utilize network resources.
1.3 Concurrency Optimization
- Runtime Configuration: Dynamically adjust the number of
tokio
worker threads to match the number of hardware cores. - Connection Management:
- HTTP/2: Fine-tune stream priority and window size management.
- HTTP/3: Dynamic stream limits and buffer optimization.
- Task Scheduling: Use
tokio
priority scheduling to prioritize critical requests. - Resource Limits: Set global connection limits and per-client connection counts to avoid overload.
1.4 Network Optimization
- TCP Optimization:
- Enable TCP Fast Open and ECN (Explicit Congestion Notification).
- Optimize TCP window size to reduce latency.
- UDP Optimization:
- Increase UDP buffer size to support high-throughput QUIC traffic.
- Enable GSO (Generic Segmentation Offload) to reduce kernel overhead.
- DNS Optimization: Integrate fast DNS resolution (e.g.,
trust-dns
) to reduce domain resolution latency. - Keep-Alive: Extend the lifespan of HTTP/2 and QUIC connections to lower reconnection costs.
1.5 Certificate Management
- Preloading: Cache all certificates at startup, reducing SNI lookup complexity to O(1).
- Hot Reloading: Implement dynamic certificate updates through file monitoring (
notify
) without requiring a restart. - ACME Integration: Support Let’s Encrypt automatic renewal to simplify certificate management.
- Certificate Priority: Optimize certificate selection logic for high-frequency domains.
1.6 Monitoring and Tuning
- Real-Time Monitoring: Use
metrics
andtracing
to log key metrics (handshake time, connection count, stream error rate, etc.). - Log Optimization: Structured logging to support distributed tracing (e.g., OpenTelemetry).
- Dynamic Tuning: Adjust parameters (e.g., maximum connection count, stream limits) via configuration files or APIs.
- Alert System: Integrate alert mechanisms (e.g., Prometheus) to promptly identify performance bottlenecks.
1.7 Load Balancing
- Multi-Instance Deployment: Support horizontal scaling by running multiple server instances.
- Load Balancer:
- TCP (HTTP/2): Use Nginx or cloud ALB.
- UDP (HTTP/3): Use load balancers that support QUIC (e.g., AWS NLB).
- Health Checks: Implement a
/health
endpoint for load balancers to check instance status. - Consistent Hashing: Ensure QUIC connection migration is allocated to the same instance.
2. Complete Implementation After Improvements
We will integrate the above optimizations into the code from the previous section to create an ultra-high-performance HTTPS server. Below is the complete code, including HTTP/3, HTTP/2, multi-domain support, and comprehensive optimizations.
2.1 Dependency Configuration
Add dependencies in Cargo.toml
to ensure support for all optimization features:
[dependencies]
hyper = { version = "1.4", features = ["http1", "http2", "server"] }
hyper-rustls = { version = "0.27", features = ["http1", "http2"] }
rustls = { version = "0.23", features = ["aws-lc-rs"] }
tokio = { version = "1.40", features = ["full"] }
rustls-pemfile = "2.1"
tokio-stream = "0.1"
quinn = "0.11"
h3 = "0.0.5"
h3-quinn = "0.0.5"
notify = "6.1" # Certificate hot reloading
metrics = "0.23"
metrics-exporter-prometheus = "0.15"
tracing = "0.1"
tracing-subscriber = "0.3"
tokio-tungstenite = "0.23" # Optional: WebSocket support
2.2 Complete Code
use hyper::service::{make_service_fn, service_fn};
use hyper::{Body, Request, Response, Server, StatusCode};
use rustls::pki_types::{CertificateDer, PrivateKeyDer, ServerName};
use rustls::server::ResolvesServerCert;
use rustls_pemfile::{certs, pkcs8_private_keys};
use std::collections::HashMap;
use std::fs::File;
use std::io::BufReader;
use std::net::SocketAddr;
use std::sync::Arc;
use tokio::net::{TcpListener, UdpSocket};
use tokio_stream::StreamExt;
use quinn::{Endpoint, ServerConfig as QuinnServerConfig};
use h3::{server::Connection as H3Connection, quic::BidiStream};
use h3_quinn::QuinnServer;
use notify::{Config, RecommendedWatcher, RecursiveMode, Watcher};
use metrics::{counter, gauge};
use metrics_exporter_prometheus::PrometheusBuilder;
use tracing::{info, warn, error};
use tracing_subscriber;
async fn handle_hyper_request(req: Request<Body>) -> Result<Response<Body>, hyper::Error> {
let host = req.headers().get("host").map(|h| h.to_str().unwrap_or("unknown")).unwrap_or("unknown");
counter!("http2_requests_total", "host" => host.to_string()).increment(1);
Ok(Response::new(Body::from(format!("Hello from {} (HTTP/2)!", host))))
}
async fn handle_h3_request(mut conn: H3Connection<QuinnServer, bytes::Bytes>) -> Result<(), Box<dyn std::error::Error>> {
while let Some((req, stream)) = conn.accept().await? {
let host = req.headers().get("host").map(|h| h.to_str().unwrap_or("unknown")).unwrap_or("unknown");
counter!("http3_requests_total", "host" => host.to_string()).increment(1);
let mut stream = stream;
let response = Response::new(Body::from(format!("Hello from {} (HTTP/3)!", host)));
h3::server::send_response(&mut stream, response).await?;
}
Ok(())
}
async fn handle_health(_req: Request<Body>) -> Result<Response<Body>, hyper::Error> {
Ok(Response::new(Body::from("OK")))
}
fn load_certs(path: &str) -> std::io::Result<Vec<CertificateDer<'static>>> {
certs(&mut BufReader::new(File::open(path)?))
.collect::<Result<Vec<_>, _>>()
.map_err(|_| std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid cert"))
}
fn load_key(path: &str) -> std::io::Result<PrivateKeyDer<'static>> {
pkcs8_private_keys(&mut BufReader::new(File::open(path)?))
.next()
.ok_or_else(|| std::io::Error::new(std::io::ErrorKind::InvalidData, "no keys found"))?
.map_err(|_| std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid key"))
.map(Into::into)
}
struct SniResolver {
certs: HashMap<String, Arc<rustls::ServerConfig>>,
}
impl SniResolver {
fn new() -> Self {
SniResolver {
certs: HashMap::new(),
}
}
fn add_cert(&mut self, domain: &str, certs: Vec<CertificateDer<'static>>, key: PrivateKeyDer<'static>) -> std::io::Result<()> {
let mut config = rustls::ServerConfig::builder()
.with_no_client_auth()
.with_single_cert(certs.clone(), key.clone())
.map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))?;
config.alpn_protocols = vec![b"h3".to_vec(), b"h2".to_vec(), b"http/1.1".to_vec()];
config.max_early_data_size = 16384;
config.cert_compression = vec![rustls::compress::brotli::Compressor::new()];
config.send_half_rtt_data = true;
self.certs.insert(domain.to_string(), Arc::new(config));
info!("Added certificate for domain: {}", domain);
Ok(())
}
fn update_cert(&mut self, domain: &str, certs: Vec<CertificateDer<'static>>, key: PrivateKeyDer<'static>) -> std::io::Result<()> {
self.add_cert(domain, certs, key)?;
info!("Updated certificate for domain: {}", domain);
Ok(())
}
}
impl ResolvesServerCert for SniResolver {
fn resolve(&self, server_name: ServerName<'_>) -> Option<Arc<rustls::ServerConfig>> {
match server_name {
ServerName::DnsName(dns_name) => {
let name = dns_name.as_ref().to_string();
let cert = self.certs.get(&name).cloned();
if cert.is_some() {
counter!("sni_resolutions_total", "domain" => name.clone()).increment(1);
} else {
warn!("No certificate found for domain: {}", name);
}
cert
}
_ => None,
}
}
}
fn build_quinn_config(sni_resolver: Arc<SniResolver>) -> QuinnServerConfig {
let mut config = rustls::ServerConfig::builder()
.with_no_client_auth()
.with_cert_resolver(sni_resolver);
config.alpn_protocols = vec![b"h3".to_vec()];
config.max_early_data_size = 16384;
config.cert_compression = vec![rustls::compress::brotli::Compressor::new()];
let mut quinn_config = QuinnServerConfig::with_crypto(Arc::new(config));
quinn_config
.transport_config(Arc::new(quinn::TransportConfig {
max_concurrent_bidi_streams: Some(100),
max_concurrent_uni_streams: Some(100),
datagram_send_buffer_size: Some(1350),
..Default::default()
}))
.use_bbr(true);
quinn_config
}
async fn watch_certificates(sni_resolver: Arc<SniResolver>, domains: Vec<(String, String, String)>) -> Result<(), Box<dyn std::error::Error>> {
let (tx, rx) = std::sync::mpsc::channel();
let mut watcher = RecommendedWatcher::new(tx, Config::default())?;
for (domain, cert_path, key_path) in &domains {
watcher.watch(std::path::Path::new(cert_path), RecursiveMode::NonRecursive)?;
watcher.watch(std::path::Path::new(key_path), RecursiveMode::NonRecursive)?;
}
for event in rx {
if let Ok(event) = event {
for (domain, cert_path, key_path) in &domains {
if event.paths.iter().any(|p| p == std::path::Path::new(cert_path) || p == std::path::Path::new(key_path)) {
match (load_certs(cert_path), load_key(key_path)) {
(Ok(certs), Ok(key)) => {
sni_resolver.update_cert(domain, certs, key)?;
}
(Err(e), _) | (_, Err(e)) => {
error!("Failed to reload certificate for {}: {}", domain, e);
}
}
}
}
}
}
Ok(())
}
#[tokio::main(flavor = "multi_thread", worker_threads = 8)]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Initialize logging
tracing_subscriber::fmt()
.with_max_level(tracing::Level::INFO)
.init();
info!("Starting high-performance HTTPS server");
// Initialize monitoring
let prometheus = PrometheusBuilder::new()
.install()
.expect("Failed to initialize Prometheus");
gauge!("server_up", 1.0);
// Initialize SNI resolver
let mut sni_resolver = SniResolver::new();
let domains = vec![
(
"a.example.com".to_string(),
"a_cert.pem".to_string(),
"a_key.pem".to_string(),
),
(
"b.example.com".to_string(),
"b_cert.pem".to_string(),
"b_key.pem".to_string(),
),
];
for (domain, cert_path, key_path) in &domains {
sni_resolver.add_cert(
domain,
load_certs(cert_path)?,
load_key(key_path)?,
)?;
}
let sni_resolver = Arc::new(sni_resolver);
// Start certificate hot reloading monitoring
let sni_resolver_clone = sni_resolver.clone();
tokio::spawn(watch_certificates(sni_resolver_clone, domains));
// HTTP/2 server (TCP)
let http2_addr = SocketAddr::from(([0, 0, 0, 0], 8443));
let http2_listener = TcpListener::bind(http2_addr).await?;
let http2_make_service = make_service_fn(|_conn| async {
Ok::<_, hyper::Error>(service_fn(|req: Request<Body>| async move {
if req.uri().path() == "/health" {
handle_health(req).await
} else {
handle_hyper_request(req).await
}
}))
});
let http2_server = Server::builder(hyper::server::accept::from_stream(tokio_stream::wrappers::TcpListenerStream::new(http2_listener)))
.http1_keep_alive(true)
.http2_max_concurrent_streams(200)
.tcp_nodelay(true)
.tcp_fastopen(true)
.max_connections(1000)
.serve(http2_make_service);
// HTTP/3 server (UDP)
let http3_addr = SocketAddr::from(([0, 0, 0, 0], 8443));
let udp_socket = {
let socket = UdpSocket::bind(http3_addr).await?;
socket.set_send_buffer_size(1048576)?;
socket.set_recv_buffer_size(1048576)?;
socket
};
let quinn_config = build_quinn_config(sni_resolver.clone());
let endpoint = Endpoint::new_server(udp_socket, quinn_config)?;
// Start servers
let http2_task = tokio::spawn(async move {
info!("HTTP/2 server running on https://{}", http2_addr);
if let Err(e) = http2_server.await {
error!("HTTP/2 server error: {}", e);
}
});
let http3_task = tokio::spawn(async move {
info!("HTTP/3 server running on https://{}", http3_addr);
while let Some(conn) = endpoint.accept().await {
let conn = match conn.await {
Ok(conn) => conn,
Err(e) => {
error!("QUIC connection error: {}", e);
continue;
}
};
counter!("http3_connections_total").increment(1);
tokio::spawn(async move {
match H3Connection::new(h3_quinn::QuinnServer::new(conn)).await {
Ok(h3_conn) => {
if let Err(e) = handle_h3_request(h3_conn).await {
error!("HTTP/3 request error: {}", e);
}
}
Err(e) => {
error!("HTTP/3 connection error: {}", e);
}
}
});
}
});
tokio::try_join!(http2_task, http3_task)?;
Ok(())
}
2.3 Optimization Implementation Details
The following are the optimization details integrated into the code:
TLS Optimization
- Enforce TLS 1.3: Configured via
rustls
to only support TLS 1.3 and disable TLS 1.2. - 0-RTT: Set
max_early_data_size = 16384
to enable early data and prevent replay attacks usingsend_half_rtt_data
. - Session Tickets: Default to enabling
TicketSwitcher
for quick reconnections. - Certificate Compression: Use Brotli to compress certificates (
rustls::compress::brotli
) to reduce handshake bandwidth. - Encryption Algorithms: Rely on
aws-lc-rs
to prioritize AES-GCM and support hardware acceleration.
QUIC Optimization
- Datagram Size: Set
datagram_send_buffer_size = 1350
to adapt to common MTUs. - Congestion Control: Enable BBR (
use_bbr(true)
) to optimize for high-latency networks. - Stream Limits: Set
max_concurrent_bidi_streams = 100
andmax_concurrent_uni_streams = 100
to prevent resource exhaustion. - Connection Migration: QUIC supports connection IDs by default to adapt to mobile network switching.
Concurrency Optimization
- Runtime: Use
multi_thread
runtime withworker_threads = 8
(adjustable based on CPU core count). - Connection Management:
- HTTP/2: Set
http2_max_concurrent_streams = 200
andmax_connections = 1000
. - HTTP/3: Limit concurrent streams via
quinn::TransportConfig
. - Health Checks: Add a
/health
endpoint to support load balancer checks. - Task Priority: Prioritize health check requests (via routing logic).
Network Optimization
- TCP:
- Enable
tcp_nodelay(true)
to disable the Nagle algorithm. - Enable
tcp_fastopen(true)
to speed up the first connection. - UDP:
- Set
send_buffer_size
andrecv_buffer_size
to 1MB to support high throughput. - Future support for GSO (requires kernel support).
- Keep-Alive: HTTP/2 enables
http1_keep_alive
, and QUIC supports long connections by default.
Certificate Management
- Preloading: Load all certificates into a
HashMap
at startup, reducing SNI lookup to O(1). - Hot Reloading:
- Monitor certificate files (e.g.,
a_cert.pem
,b_cert.pem
) usingnotify
. - Update
SniResolver
dynamically upon changes without requiring a restart. - ACME: Not directly integrated in the code, but can be achieved using external tools (e.g.,
acme-client
) to generate certificates.
Monitoring and Tuning
- Metrics:
- Use
metrics
to log request counts (http2_requests_total
,http3_requests_total
), SNI resolutions (sni_resolutions_total
), and connection counts (http3_connections_total
). - Expose Prometheus endpoints via
metrics-exporter-prometheus
(default:9090
). - Logs:
- Use
tracing
for structured logging, logging INFO level for startup and certificate updates, and ERROR level for exceptions. - Support distributed tracing (extendable to OpenTelemetry).
- Dynamic Configuration: Support runtime adjustments via
SniResolver::update_cert
.
Load Balancing
- Health Checks: The
/health
endpoint returnsOK
for load balancer checks. - Multi-Instance: The code supports multi-instance deployment, with HTTP/2 using TCP load balancing and HTTP/3 using UDP load balancing.
- Consistency: QUIC connection IDs ensure allocation to the same instance during migration.
3. Deployment and Testing
3.1 Deployment Preparation
- Certificates:
openssl req -x509 -newkey rsa:2048 -keyout a_key.pem -out a_cert.pem -days 365 -nodes -subj "/CN=a.example.com"
openssl req -x509 -newkey rsa:2048 -keyout b_key.pem -out b_cert.pem -days 365 -nodes -subj "/CN=b.example.com"
- For production environments, it is recommended to use Let’s Encrypt:
certbot certonly --standalone -d a.example.com -d b.example.com
- DNS:
- Local testing:
127.0.0.1 a.example.com
127.0.0.1 b.example.com
- Public: Configure A records pointing to the server IP.
- Firewall:
sudo ufw allow 8443/tcp
sudo ufw allow 8443/udp
sudo ufw allow 9090/tcp # Prometheus
- System Optimization:
- Increase file descriptor limits:
ulimit -n 65535
- Optimize network parameters:
sysctl -w net.core.rmem_max=8388608
sysctl -w net.core.wmem_max=8388608
3.2 Testing
- HTTP/2:
curl --http2 -k https://a.example.com:8443
curl --http2 -k https://b.example.com:8443
Output:
Hello from a.example.com (HTTP/2)!
Hello from b.example.com (HTTP/2)!
- HTTP/3:
curl --http3-only -k https://a.example.com:8443
curl --http3-only -k https://b.example.com:8443
Output:
Hello from a.example.com (HTTP/3)!
Hello from b.example.com (HTTP/3)!
- Health Check:
curl http://<server-ip>:8443/health
# Output: OK
- Monitoring:
- Access the Prometheus endpoint:
http://<server-ip>:9090/metrics
- Example metrics:
http2_requests_total{host="a.example.com"} 10
http3_requests_total{host="b.example.com"} 5
sni_resolutions_total{domain="a.example.com"} 15
- Certificate Hot Reloading:
- Modify
a_cert.pem
(e.g., regenerate), and the server will automatically load:
openssl req -x509 -newkey rsa:2048 -keyout a_key.pem -out a_cert.pem -days 365 -nodes -subj "/CN=a.example.com"
- Check logs:
[INFO] Updated certificate for domain: a.example.com
4. Summary and Extensions
Through comprehensive optimizations, we have built an ultra-high-performance HTTPS server with the following features:
- Public Network Access: Listening on
0.0.0.0:8443
(TCP and UDP), supporting global access. - HTTP/3 and HTTP/2: Implementing the latest protocols via
quinn
andhyper
. - Multi-Domain: SNI support for
a.example.com
andb.example.com
, with dynamic certificate updates. - Ultra-High Performance:
- TLS: Enforce TLS 1.3, 0-RTT, certificate compression.
- QUIC: BBR, stream limits, connection migration.
- Concurrency: Multithreading, connection pooling, priority scheduling.
- Network: TCP/UDP optimizations, Keep-Alive.
- Certificates: Hot reloading, preloading.
- Monitoring: Prometheus metrics, structured logging.
- Load Balancing: Health checks, multi-instance support.
Future Extensions
- ACME Integration:
- Use
rustls-acme
oracme-client
to automatically obtain Let’s Encrypt certificates.
- Integrate
tokio-tungstenite
to support encrypted WebSocket.
- Experimental support for multipath features of
quinn
to optimize mobile networks.
- Use
tokio-uring
orio_uring
to reduce data copying.
- Integrate Kubernetes or Nomad to support automatic scaling.
Considerations
- HTTP/3 Stability:
h3
andh3-quinn
are experimental libraries; thorough testing is required in production environments. - Ports: It is recommended to use port 443 for public deployment (requires root privileges or
setcap
). - Certificates: Use valid certificates in production environments; self-signed certificates are for testing.
- Monitoring: It is recommended to integrate Grafana for visualizing Prometheus data.
We hope that the optimization solutions and code examples in this article provide developers with a complete blueprint for building production-grade HTTPS servers, aiding in the construction of secure and efficient web services!
No matter where you are
You are no longer alone
Long press to recognize the QR code to follow us