As a junior computer science student, I needed to build a high-concurrency web service for my course project. After extensive framework research and performance testing, I discovered a shocking fact: a certain Rust-based lightweight framework completely crushed mainstream choices in performance tests.
Setting Up My Test Environment
My test machine configuration wasn't top-tier: Intel i7-10700K, 32GB RAM, running Windows 11. To ensure fair test results, I used identical test conditions, including the same port, same response content, and same Keep-Alive settings.
For testing tools, I chose industry-standard wrk and Apache Bench (ab), which have widespread recognition in the pressure testing field. I kept all test code minimized to avoid business logic interference with performance testing.
use hyperlane::*;
async fn request_middleware(ctx: Context) {
let socket_addr: String = ctx.get_socket_addr_or_default_string().await;
ctx.set_response_header(SERVER, HYPERLANE)
.await
.set_response_header(CONNECTION, KEEP_ALIVE)
.await
.set_response_header(CONTENT_TYPE, TEXT_PLAIN)
.await
.set_response_header("SocketAddr", socket_addr)
.await;
}
async fn response_middleware(ctx: Context) {
let _ = ctx.send().await;
}
#[methods(get, post)]
async fn root_route(ctx: Context) {
ctx.set_response_status_code(200)
.await
.set_response_body("Hello World")
.await;
}
#[tokio::main]
async fn main() {
let server: Server = Server::new();
server.host("0.0.0.0").await;
server.port(60000).await;
server.enable_nodelay().await;
server.disable_linger().await;
server.http_line_buffer_size(4096).await;
server.request_middleware(request_middleware).await;
server.response_middleware(response_middleware).await;
server.route("/", root_route).await;
server.run().await.unwrap();
}
This test server code demonstrates the framework's simplicity. I built a complete HTTP server with middleware support and routing in less than 30 lines of code.
wrk Pressure Testing: Stunning Results
I conducted wrk testing with 360 concurrent connections for 60 seconds. The test command was:
wrk -c360 -d60s http://127.0.0.1:60000/
The results amazed me:
Hyperlane Framework Test Results:
Running 1m test @ http://127.0.0.1:60000/
2 threads and 360 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 1.46ms 7.74ms 230.59ms 99.57%
Req/Sec 163.12k 9.54k 187.65k 67.75%
19476349 requests in 1.00m, 1.94GB read
Requests/sec: 324323.71
Transfer/sec: 33.10MB
QPS reached 324,323! I double-checked this number several times. Latency was controlled at an average of 1.46ms, with 99.57% of requests within this range - excellent stability performance.
To verify this result's authenticity, I simultaneously tested several other well-known frameworks:
Tokio Native Implementation:
- QPS: 340,130.92
- Average Latency: 1.22ms
Rocket Framework:
- QPS: 298,945.31
- Average Latency: 1.42ms
Rust Standard Library Implementation:
- QPS: 291,218.96
- Average Latency: 1.64ms
Gin Framework (Go):
- QPS: 242,570.16
- Average Latency: 1.67ms
Go Standard Library:
- QPS: 234,178.93
- Average Latency: 1.58ms
Node.js Standard Library:
- QPS: 139,412.13
- Average Latency: 2.58ms
From this data, Hyperlane's performance is second only to Tokio's native implementation. Considering that Hyperlane provides complete web framework functionality (routing, middleware, WebSocket support, etc.) while Tokio is just the underlying async runtime, this performance is remarkable.
Apache Bench Testing: Verifying High Concurrency Capability
To further verify the framework's high-concurrency processing capability, I used Apache Bench for extreme testing with 1000 concurrent connections and 1 million requests:
ab -n 1000000 -c 1000 -r -k http://127.0.0.1:60000/
Hyperlane Framework ab Test Results:
Server Hostname: 127.0.0.1
Server Port: 60000
Document Path: /
Document Length: 5 bytes
Concurrency Level: 1000
Time taken for tests: 3.251 seconds
Complete requests: 1000000
Failed requests: 0
Keep-Alive requests: 1000000
Total transferred: 107000000 bytes
HTML transferred: 5000000 bytes
Requests per second: 307568.90 [#/sec] (mean)
Time per request: 3.251 [ms] (mean)
Time per request: 0.003 [ms] (mean, across all concurrent requests)
Transfer rate: 32138.55 [Kbytes/sec] received
One million requests completed in 3.251 seconds with QPS reaching 307,568 and zero failed requests. This stability is especially valuable in high-concurrency scenarios.
Comparing other frameworks' ab test results:
- Tokio: 308,596.26 QPS
- Hyperlane: 307,568.90 QPS
- Rocket: 267,931.52 QPS
- Rust Standard Library: 260,514.56 QPS
- Go Standard Library: 226,550.34 QPS
- Gin: 224,296.16 QPS
- Node.js: 85,357.18 QPS
Hyperlane again demonstrated performance close to Tokio's native implementation while providing complete web development functionality.
Deep Analysis: Why Such Excellent Performance
Through analyzing Hyperlane's source code and architectural design, I discovered several key performance optimization points:
1. Zero-Copy Design
// Hyperlane's request body handling
async fn handle_request_body(ctx: Context) {
let body: Vec<u8> = ctx.get_request_body().await;
// Direct raw byte operations, avoiding unnecessary string conversions
let response_body = process_raw_bytes(&body);
ctx.set_response_body(response_body).await;
}
fn process_raw_bytes(data: &[u8]) -> Vec<u8> {
// Direct byte-level data processing, avoiding encoding/decoding overhead
data.iter().map(|&b| b.wrapping_add(1)).collect()
}
2. Intelligent TCP Parameter Tuning
// Server configuration shows underlying network optimization
let server: Server = Server::new();
server.enable_nodelay().await; // Disable Nagle algorithm, reduce latency
server.disable_linger().await; // Optimize connection closing behavior
server.http_line_buffer_size(4096).await; // Optimize buffer size
These configurations seem simple, but each is carefully tuned. Disabling the Nagle algorithm can significantly reduce small packet transmission latency, which is crucial for web service response times.
3. Efficient Memory Management
// Context design avoids unnecessary memory allocation
#[derive(Clone, Default)]
pub struct Context(pub(super) ArcRwLock<InnerContext>);
// Using Arc and RwLock for efficient concurrent access
impl Context {
pub async fn get_request_method(&self) -> Method {
let inner = self.0.read().await;
inner.request.method.clone() // Only clone necessary data
}
}
Context uses a combination of Arc (atomic reference counting) and RwLock (read-write lock), ensuring thread safety while maximizing concurrent read performance.
4. Deep Async I/O Optimization
// Middleware async processing demonstrates framework's concurrency capability
async fn logging_middleware(ctx: Context) {
let start = std::time::Instant::now();
let method = ctx.get_request_method().await;
let uri = ctx.get_request_uri().await;
// Async processing doesn't block other requests
tokio::spawn(async move {
let duration = start.elapsed();
println!("{} {} - {}ms", method, uri, duration.as_millis());
});
}
The framework fully leverages Rust's async features, with each request's processing being non-blocking, allowing a single thread to handle thousands of concurrent connections simultaneously.
Performance in Real Projects
In my course project, I built a simulated e-commerce API service including user authentication, product queries, order processing, and other functions. Even with complex business logic, Hyperlane maintained excellent performance:
use hyperlane::*;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use tokio::sync::RwLock;
use std::sync::Arc;
#[derive(Serialize, Deserialize, Clone)]
struct Product {
id: u32,
name: String,
price: f64,
stock: u32,
}
#[derive(Serialize, Deserialize)]
struct Order {
user_id: u32,
product_id: u32,
quantity: u32,
}
// Simulated database
type ProductDB = Arc<RwLock<HashMap<u32, Product>>>;
type OrderDB = Arc<RwLock<Vec<Order>>>;
async fn init_database() -> (ProductDB, OrderDB) {
let mut products = HashMap::new();
products.insert(1, Product {
id: 1,
name: "iPhone 15".to_string(),
price: 999.99,
stock: 100,
});
products.insert(2, Product {
id: 2,
name: "MacBook Pro".to_string(),
price: 2499.99,
stock: 50,
});
(
Arc::new(RwLock::new(products)),
Arc::new(RwLock::new(Vec::new()))
)
}
#[get]
async fn get_products(ctx: Context) {
let products_db = ctx.get_attribute::<ProductDB>("products_db").await.unwrap();
let products = products_db.read().await;
let product_list: Vec<Product> = products.values().cloned().collect();
let json_response = serde_json::to_string(&product_list).unwrap();
ctx.set_response_header(CONTENT_TYPE, APPLICATION_JSON)
.await
.set_response_status_code(200)
.await
.set_response_body(json_response)
.await;
}
This e-commerce API maintained tens of thousands of requests per second processing capability in my tests, even involving complex data operations and JSON serialization.
Project Repository: GitHub
Author Email: [email protected]
Top comments (0)