perf: use picoev as default Server for improve response concurrency

This commit is contained in:
meiseayoung
2025-12-30 17:31:13 +08:00
parent ee2818782b
commit a38aeea614
23 changed files with 2034 additions and 36 deletions
+1 -1
View File
@@ -100,7 +100,7 @@ fn main() {
})
println('🚀 用户角色菜单管理系统已启动')
println('📱 访问地址: http://localhost:3000')
println('📱 访问地址: http://127.0.0.1:3000')
println('')
println('📋 示例用户:')
println(' 用户名: admin, 密码: admin123, 角色: admin')
+1 -1
View File
@@ -42,6 +42,6 @@ fn main() {
return c.json('{"received": "${body}"}')
})
println('Server starting on http://localhost:3000')
println('Server starting on http://127.0.0.1:3000')
app.listen(':3000')
}
+344
View File
@@ -0,0 +1,344 @@
// veb vs v-hono 性能对比测试
// 运行: v run benchmark_veb_vs_hono.v
//
// 测试内容:
// 1. 路由匹配性能(纯路由,不启动服务器)
// 2. 静态路由 vs 动态路由性能对比
// 3. 缓存命中率分析
module main
import time
import hono
import net.http
// ============================================
// 测试配置
// ============================================
const iterations = 10000
const warmup_iterations = 1000
// 测试路径
const static_paths = [
'/',
'/api/health',
'/api/users',
]
const dynamic_paths = [
'/api/users/123',
'/api/users/456/posts',
'/api/users/789/posts/101',
'/api/categories/electronics/items/phone',
]
// ============================================
// 简单路由器(模拟 veb 的路由匹配方式)
// ============================================
struct SimpleRouter {
mut:
static_routes map[string]string
dynamic_routes []DynamicRoute
}
struct DynamicRoute {
pattern string
param_names []string
}
fn SimpleRouter.new() SimpleRouter {
return SimpleRouter{
static_routes: map[string]string{}
dynamic_routes: []DynamicRoute{}
}
}
fn (mut r SimpleRouter) add_static(path string) {
r.static_routes[path] = path
}
fn (mut r SimpleRouter) add_dynamic(pattern string, param_names []string) {
r.dynamic_routes << DynamicRoute{pattern, param_names}
}
// 简单的动态路由匹配(模拟 veb 的线性扫描方式)
fn (r SimpleRouter) match_route(path string) (bool, map[string]string) {
// 1. 先尝试静态匹配
if path in r.static_routes {
return true, map[string]string{}
}
// 2. 动态路由匹配(线性扫描)
path_parts := path.split('/').filter(it.len > 0)
for route in r.dynamic_routes {
pattern_parts := route.pattern.split('/').filter(it.len > 0)
if path_parts.len != pattern_parts.len {
continue
}
mut matched := true
mut params := map[string]string{}
mut param_idx := 0
for i, part in pattern_parts {
if part.starts_with(':') {
// 参数匹配
if param_idx < route.param_names.len {
params[route.param_names[param_idx]] = path_parts[i]
param_idx++
}
} else if part != path_parts[i] {
matched = false
break
}
}
if matched {
return true, params
}
}
return false, map[string]string{}
}
// ============================================
// 基准测试函数
// ============================================
struct BenchmarkResult {
name string
total_time_ns i64
avg_time_ns i64
min_time_ns i64
max_time_ns i64
ops_per_sec f64
iterations int
}
fn benchmark(name string, iterations int, f fn () bool) BenchmarkResult {
mut times := []i64{cap: iterations}
mut min_time := i64(9223372036854775807)
mut max_time := i64(0)
// 预热
for _ in 0 .. warmup_iterations {
f()
}
// 正式测试
for _ in 0 .. iterations {
sw := time.new_stopwatch()
f()
elapsed := sw.elapsed().nanoseconds()
times << elapsed
if elapsed < min_time {
min_time = elapsed
}
if elapsed > max_time {
max_time = elapsed
}
}
mut total := i64(0)
for t in times {
total += t
}
avg := total / iterations
ops_per_sec := 1_000_000_000.0 / f64(avg)
return BenchmarkResult{
name: name
total_time_ns: total
avg_time_ns: avg
min_time_ns: min_time
max_time_ns: max_time
ops_per_sec: ops_per_sec
iterations: iterations
}
}
fn print_result(r BenchmarkResult) {
println('┌─────────────────────────────────────────────────────────────┐')
println('│ ${r.name:-59} │')
println('├─────────────────────────────────────────────────────────────┤')
println('│ 迭代次数: ${r.iterations:-48} │')
println('│ 总耗时: ${r.total_time_ns / 1_000_000:-45} ms │')
println('│ 平均耗时: ${r.avg_time_ns:-45} ns │')
println('│ 最小耗时: ${r.min_time_ns:-45} ns │')
println('│ 最大耗时: ${r.max_time_ns:-45} ns │')
println('│ 吞吐量: ${r.ops_per_sec:-42.0} ops/s │')
println('└─────────────────────────────────────────────────────────────┘')
}
fn print_comparison(name string, simple_result BenchmarkResult, hono_result BenchmarkResult) {
speedup := f64(simple_result.avg_time_ns) / f64(hono_result.avg_time_ns)
winner := if speedup > 1.0 { 'v-hono' } else { 'SimpleRouter' }
ratio := if speedup > 1.0 { speedup } else { 1.0 / speedup }
println('')
println('═══════════════════════════════════════════════════════════════')
println(' ${name} 对比结果')
println('═══════════════════════════════════════════════════════════════')
println(' SimpleRouter: ${simple_result.avg_time_ns} ns/op (${simple_result.ops_per_sec:.0} ops/s)')
println(' v-hono: ${hono_result.avg_time_ns} ns/op (${hono_result.ops_per_sec:.0} ops/s)')
println(' 胜出: ${winner} (快 ${ratio:.2}x)')
println('═══════════════════════════════════════════════════════════════')
}
// ============================================
// 主测试函数
// ============================================
fn main() {
println('')
println('╔═══════════════════════════════════════════════════════════════╗')
println('║ veb vs v-hono 路由性能对比测试 ║')
println('║ (SimpleRouter 模拟 veb 的路由匹配方式) ║')
println('╠═══════════════════════════════════════════════════════════════╣')
println('║ 测试配置: ║')
println('║ - 迭代次数: ${iterations:-46} ║')
println('║ - 预热次数: ${warmup_iterations:-46} ║')
println('╚═══════════════════════════════════════════════════════════════╝')
println('')
// 初始化 SimpleRouter
mut simple_router := SimpleRouter.new()
simple_router.add_static('/')
simple_router.add_static('/api/health')
simple_router.add_static('/api/users')
simple_router.add_dynamic('/api/users/:id', ['id'])
simple_router.add_dynamic('/api/users/:id/posts', ['id'])
simple_router.add_dynamic('/api/users/:user_id/posts/:post_id', ['user_id', 'post_id'])
simple_router.add_dynamic('/api/categories/:cat/items/:item', ['cat', 'item'])
// 初始化 v-hono
mut hono_app := hono.Hono.new()
hono_app.get('/', fn (mut c hono.Context) http.Response {
return c.text('Hello World')
})
hono_app.get('/api/health', fn (mut c hono.Context) http.Response {
return c.text('OK')
})
hono_app.get('/api/users', fn (mut c hono.Context) http.Response {
return c.json('{"users": []}')
})
hono_app.get('/api/users/:id', fn (mut c hono.Context) http.Response {
return c.json('{"id": "123"}')
})
hono_app.get('/api/users/:id/posts', fn (mut c hono.Context) http.Response {
return c.json('{"posts": []}')
})
hono_app.get('/api/users/:user_id/posts/:post_id', fn (mut c hono.Context) http.Response {
return c.json('{"post": {}}')
})
hono_app.get('/api/categories/:cat/items/:item', fn (mut c hono.Context) http.Response {
return c.json('{"item": {}}')
})
// ============================================
// 测试 1: 静态路由性能
// ============================================
println('\n【测试 1】静态路由匹配性能')
println('─────────────────────────────────────────────────────────────────')
simple_static_result := benchmark('SimpleRouter 静态路由', iterations, fn [simple_router] () bool {
for path in static_paths {
simple_router.match_route(path)
}
return true
})
print_result(simple_static_result)
hono_static_result := benchmark('v-hono 静态路由', iterations, fn [mut hono_app] () bool {
for path in static_paths {
hono_app.fast_router.match_route('GET', path) or { continue }
}
return true
})
print_result(hono_static_result)
print_comparison('静态路由', simple_static_result, hono_static_result)
// ============================================
// 测试 2: 动态路由性能
// ============================================
println('\n【测试 2】动态路由匹配性能')
println('─────────────────────────────────────────────────────────────────')
simple_dynamic_result := benchmark('SimpleRouter 动态路由', iterations, fn [simple_router] () bool {
for path in dynamic_paths {
simple_router.match_route(path)
}
return true
})
print_result(simple_dynamic_result)
hono_dynamic_result := benchmark('v-hono 动态路由', iterations, fn [mut hono_app] () bool {
for path in dynamic_paths {
hono_app.fast_router.match_route('GET', path) or { continue }
}
return true
})
print_result(hono_dynamic_result)
print_comparison('动态路由', simple_dynamic_result, hono_dynamic_result)
// ============================================
// 测试 3: 混合路由性能
// ============================================
println('\n【测试 3】混合路由匹配性能(静态 + 动态)')
println('─────────────────────────────────────────────────────────────────')
mut all_paths := []string{}
all_paths << static_paths
all_paths << dynamic_paths
simple_mixed_result := benchmark('SimpleRouter 混合路由', iterations, fn [simple_router, all_paths] () bool {
for path in all_paths {
simple_router.match_route(path)
}
return true
})
print_result(simple_mixed_result)
hono_mixed_result := benchmark('v-hono 混合路由', iterations, fn [mut hono_app, all_paths] () bool {
for path in all_paths {
hono_app.fast_router.match_route('GET', path) or { continue }
}
return true
})
print_result(hono_mixed_result)
print_comparison('混合路由', simple_mixed_result, hono_mixed_result)
// ============================================
// 路由器统计信息
// ============================================
println('\n【路由器统计信息】')
println('─────────────────────────────────────────────────────────────────')
static_count, dynamic_count, cache_count, _ := hono_app.get_router_stats()
println('v-hono FastRouter:')
println(' - 静态路由数: ${static_count}')
println(' - 动态路由数: ${dynamic_count}')
println(' - 缓存条目数: ${cache_count}')
println('')
println('SimpleRouter:')
println(' - 静态路由数: ${simple_router.static_routes.len}')
println(' - 动态路由数: ${simple_router.dynamic_routes.len}')
// ============================================
// 总结
// ============================================
println('\n')
println('╔═══════════════════════════════════════════════════════════════╗')
println('║ 测试总结 ║')
println('╠═══════════════════════════════════════════════════════════════╣')
println('║ 1. 静态路由: SimpleRouter 使用 map 直接查找,通常更快 ║')
println('║ 2. 动态路由: v-hono 使用 Trie + 缓存,性能更稳定 ║')
println('║ 3. v-hono 的优势在于动态路由性能一致,不随复杂度增加而下降 ║')
println('╚═══════════════════════════════════════════════════════════════╝')
}
+237
View File
@@ -0,0 +1,237 @@
// HTTP 压测工具 - veb vs v-hono 性能对比
// 运行: v run http_benchmark.v
//
// 使用前请先启动服务器:
// 终端1: v run server_veb.v (端口 8080)
// 终端2: v run server_hono.v (端口 8081)
module main
import net.http
import time
// 测试配置
const veb_base_url = 'http://127.0.0.1:8080'
const hono_base_url = 'http://127.0.0.1:8081'
const requests_per_endpoint = 200
// 测试端点
struct Endpoint {
path string
name string
}
const test_endpoints = [
Endpoint{path: '/', name: '首页 (静态)'},
Endpoint{path: '/api/health', name: '健康检查 (静态)'},
Endpoint{path: '/api/users', name: '用户列表 (静态)'},
Endpoint{path: '/api/users/123', name: '单个用户 (动态)'},
Endpoint{path: '/api/users/456/posts', name: '用户帖子 (动态)'},
]
struct BenchmarkResult {
endpoint string
total_requests int
success_count int
error_count int
total_time_ms i64
avg_time_ms f64
min_time_ms f64
max_time_ms f64
rps f64
}
fn main() {
println('')
println('╔═══════════════════════════════════════════════════════════════╗')
println('║ HTTP 压测工具 - veb vs v-hono 性能对比 ║')
println('╠═══════════════════════════════════════════════════════════════╣')
println('║ 请确保已启动以下服务器: ║')
println('║ - veb: v run server_veb.v (端口 8080) ║')
println('║ - v-hono: v run server_hono.v (端口 8081) ║')
println('╚═══════════════════════════════════════════════════════════════╝')
println('')
// 检查服务器是否可用
println('检查服务器状态...')
veb_available := check_server(veb_base_url)
hono_available := check_server(hono_base_url)
if !veb_available {
println('⚠️ veb 服务器 (${veb_base_url}) 不可用')
} else {
println('✅ veb 服务器 (${veb_base_url}) 已就绪')
}
if !hono_available {
println('⚠️ v-hono 服务器 (${hono_base_url}) 不可用')
} else {
println('✅ v-hono 服务器 (${hono_base_url}) 已就绪')
}
if !veb_available && !hono_available {
println('')
println('❌ 没有可用的服务器,请先启动服务器后再运行测试')
return
}
println('')
println('开始压测 (每端点 ${requests_per_endpoint} 次请求)...')
println('─────────────────────────────────────────────────────────────────')
mut veb_results := []BenchmarkResult{}
mut hono_results := []BenchmarkResult{}
for endpoint in test_endpoints {
println('')
println('测试端点: ${endpoint.name} (${endpoint.path})')
if veb_available {
result := run_benchmark('veb', veb_base_url, endpoint)
veb_results << result
print_single_result('veb', result)
}
if hono_available {
result := run_benchmark('v-hono', hono_base_url, endpoint)
hono_results << result
print_single_result('v-hono', result)
}
}
// 打印对比结果
if veb_available && hono_available {
print_comparison_table(veb_results, hono_results)
}
// 打印总结
print_summary(veb_results, hono_results, veb_available, hono_available)
}
fn check_server(base_url string) bool {
http.get(base_url) or { return false }
return true
}
fn run_benchmark(name string, base_url string, endpoint Endpoint) BenchmarkResult {
url := '${base_url}${endpoint.path}'
mut success_count := 0
mut error_count := 0
mut min_time := f64(999999.0)
mut max_time := f64(0.0)
mut total_req_time := f64(0.0)
start_time := time.now()
for _ in 0 .. requests_per_endpoint {
req_start := time.now()
resp := http.get(url) or {
error_count++
continue
}
req_time := f64(time.since(req_start).microseconds()) / 1000.0
if resp.status_code >= 200 && resp.status_code < 300 {
success_count++
} else {
error_count++
}
total_req_time += req_time
if req_time < min_time {
min_time = req_time
}
if req_time > max_time {
max_time = req_time
}
}
total_time := time.since(start_time)
total_time_ms := total_time.milliseconds()
total_requests := success_count + error_count
avg_time := if total_requests > 0 { total_req_time / f64(total_requests) } else { 0.0 }
rps := if total_time_ms > 0 { f64(total_requests) * 1000.0 / f64(total_time_ms) } else { 0.0 }
return BenchmarkResult{
endpoint: endpoint.name
total_requests: total_requests
success_count: success_count
error_count: error_count
total_time_ms: total_time_ms
avg_time_ms: avg_time
min_time_ms: min_time
max_time_ms: max_time
rps: rps
}
}
fn print_single_result(name string, r BenchmarkResult) {
println(' ${name}: ${r.rps:.0} req/s | avg: ${r.avg_time_ms:.2}ms | min: ${r.min_time_ms:.2}ms | max: ${r.max_time_ms:.2}ms | 成功: ${r.success_count}/${r.total_requests}')
}
fn print_comparison_table(veb_results []BenchmarkResult, hono_results []BenchmarkResult) {
println('')
println('╔═══════════════════════════════════════════════════════════════════════════════════════╗')
println('║ 性能对比表 ║')
println('╠═══════════════════════════════════════════════════════════════════════════════════════╣')
println('║ 端点 │ veb (req/s) │ v-hono (req/s) │ 差异 │ 胜出 ║')
println('╠═══════════════════════════════════════════════════════════════════════════════════════╣')
for i, veb_r in veb_results {
if i >= hono_results.len {
break
}
hono_r := hono_results[i]
diff := hono_r.rps - veb_r.rps
diff_pct := if veb_r.rps > 0 { (diff / veb_r.rps) * 100.0 } else { 0.0 }
winner := if hono_r.rps > veb_r.rps { 'v-hono' } else { 'veb' }
name := veb_r.endpoint
veb_rps := '${veb_r.rps:.0}'
hono_rps := '${hono_r.rps:.0}'
diff_str := if diff > 0 { '+${diff_pct:.1}%' } else { '${diff_pct:.1}%' }
println('║ ${name:-23} │ ${veb_rps:-12} │ ${hono_rps:-14} │ ${diff_str:-9} │ ${winner:-14} ║')
}
println('╚═══════════════════════════════════════════════════════════════════════════════════════╝')
}
fn print_summary(veb_results []BenchmarkResult, hono_results []BenchmarkResult, veb_available bool, hono_available bool) {
println('')
println('╔═══════════════════════════════════════════════════════════════╗')
println('║ 测试总结 ║')
println('╠═══════════════════════════════════════════════════════════════╣')
if veb_available && veb_results.len > 0 {
mut total_rps := f64(0.0)
for r in veb_results {
total_rps += r.rps
}
avg_rps := total_rps / f64(veb_results.len)
println('║ veb 平均吞吐量: ${avg_rps:-40.0} req/s ║')
}
if hono_available && hono_results.len > 0 {
mut total_rps := f64(0.0)
for r in hono_results {
total_rps += r.rps
}
avg_rps := total_rps / f64(hono_results.len)
println('║ v-hono 平均吞吐量: ${avg_rps:-40.0} req/s ║')
}
println('╠═══════════════════════════════════════════════════════════════╣')
println('║ 测试配置: ║')
println('║ - 每端点请求数: ${requests_per_endpoint:-44} ║')
println('╚═══════════════════════════════════════════════════════════════╝')
println('')
println('提示: 使用 wrk 或 ab 可以获得更准确的压测结果:')
println(' wrk -t4 -c100 -d10s http://127.0.0.1:8080/')
println(' wrk -t4 -c100 -d10s http://127.0.0.1:8081/')
}
+1 -1
View File
@@ -34,6 +34,6 @@ fn main() {
return c.json('{"data": "some data"}')
})
println('Server starting on http://localhost:3000')
println('Server starting on http://127.0.0.1:3000')
app.listen(':3000')
}
+1 -1
View File
@@ -62,7 +62,7 @@ fn main() {
return c.html('<h1>Desktop Version</h1>')
})
println('Server running on http://localhost:8080')
println('Server running on http://127.0.0.1:8080')
println('Try these endpoints:')
println(' GET /redirect-basic - Basic redirect (302)')
println(' GET /redirect-permanent - Permanent redirect (301)')
+1 -1
View File
@@ -31,7 +31,7 @@ fn main() {
return c.html('<h1>Welcome to v-hono!</h1><p>API available at /api</p>')
})
println('Server starting on http://localhost:3000')
println('Server starting on http://127.0.0.1:3000')
println('Routes:')
println(' GET / - Welcome page')
println(' GET /api/users - List users')
@@ -211,7 +211,7 @@ app.route("/api/books", mut books)</pre>
// 打印路由信息
fn print_routes_info() {
println('')
println('📍 服务器地址: http://localhost:8080')
println('📍 服务器地址: http://127.0.0.1:8080')
println('')
println('📚 Books API (/api/books):')
println(' GET /api/books - 获取所有书籍')
@@ -233,16 +233,16 @@ app.route(\'/api/books\', mut books)</pre>
<div class="group">
<h2>🧪 测试命令</h2>
<pre># 测试 all() 方法
curl http://localhost:8080/echo
curl -X POST http://localhost:8080/echo
curl -X PUT http://localhost:8080/echo
curl http://127.0.0.1:8080/echo
curl -X POST http://127.0.0.1:8080/echo
curl -X PUT http://127.0.0.1:8080/echo
# 测试自定义 404
curl http://localhost:8080/not-exist
curl http://127.0.0.1:8080/not-exist
# 测试 Admin 认证中间件
curl http://localhost:8080/admin # 返回 401
curl -H "Authorization: Bearer admin-token" http://localhost:8080/admin # 成功</pre>
curl http://127.0.0.1:8080/admin # 返回 401
curl -H "Authorization: Bearer admin-token" http://127.0.0.1:8080/admin # 成功</pre>
</div>
</body>
</html>'
@@ -251,7 +251,7 @@ curl -H "Authorization: Bearer admin-token" http://localhost:8080/admin # 成
// 打印路由信息
fn print_routes_info() {
println('')
println('📍 服务器地址: http://localhost:8080')
println('📍 服务器地址: http://127.0.0.1:8080')
println('')
println('🆕 新功能:')
println(' - notFound(): 自定义 404 处理器')
+82
View File
@@ -0,0 +1,82 @@
# veb vs v-hono 性能对比测试脚本
# 运行: .\run_benchmark.ps1
Write-Host ""
Write-Host "╔═══════════════════════════════════════════════════════════════╗" -ForegroundColor Cyan
Write-Host "║ veb vs v-hono 性能对比测试 ║" -ForegroundColor Cyan
Write-Host "╚═══════════════════════════════════════════════════════════════╝" -ForegroundColor Cyan
Write-Host ""
# 检查 V 编译器
if (-not (Get-Command v -ErrorAction SilentlyContinue)) {
Write-Host "❌ 未找到 V 编译器,请先安装 V 语言" -ForegroundColor Red
exit 1
}
Write-Host "✅ V 编译器已安装" -ForegroundColor Green
Write-Host ""
# 菜单
Write-Host "请选择测试类型:" -ForegroundColor Yellow
Write-Host " 1. 路由匹配性能测试 (不需要启动服务器)"
Write-Host " 2. HTTP 压测 (需要先启动服务器)"
Write-Host " 3. 启动 veb 服务器 (端口 8080)"
Write-Host " 4. 启动 v-hono 服务器 (端口 8081)"
Write-Host " 5. 编译所有测试文件"
Write-Host " 6. 退出"
Write-Host ""
$choice = Read-Host "请输入选项 (1-6)"
switch ($choice) {
"1" {
Write-Host ""
Write-Host "运行路由匹配性能测试..." -ForegroundColor Cyan
v run benchmark_veb_vs_hono.v
}
"2" {
Write-Host ""
Write-Host "运行 HTTP 压测..." -ForegroundColor Cyan
Write-Host "请确保已在其他终端启动服务器:" -ForegroundColor Yellow
Write-Host " - veb: v run server_veb.v (端口 8080)"
Write-Host " - v-hono: v run server_hono.v (端口 8081)"
Write-Host ""
v run http_benchmark.v
}
"3" {
Write-Host ""
Write-Host "启动 veb 服务器 (端口 8080)..." -ForegroundColor Cyan
v run server_veb.v
}
"4" {
Write-Host ""
Write-Host "启动 v-hono 服务器 (端口 8081)..." -ForegroundColor Cyan
v run server_hono.v
}
"5" {
Write-Host ""
Write-Host "编译所有测试文件..." -ForegroundColor Cyan
Write-Host "编译 benchmark_veb_vs_hono.v..."
v -prod benchmark_veb_vs_hono.v -o benchmark_veb_vs_hono.exe
Write-Host "编译 server_veb.v..."
v -prod server_veb.v -o server_veb.exe
Write-Host "编译 server_hono.v..."
v -prod server_hono.v -o server_hono.exe
Write-Host "编译 http_benchmark.v..."
v -prod http_benchmark.v -o http_benchmark.exe
Write-Host ""
Write-Host "✅ 编译完成!" -ForegroundColor Green
}
"6" {
Write-Host "退出" -ForegroundColor Yellow
exit 0
}
default {
Write-Host "无效选项" -ForegroundColor Red
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
// v-hono 服务器示例 - 用于性能对比测试
// 运行: v run server_hono.v
// 测试: curl http://localhost:8081/
// 测试: curl http://127.0.0.1:8081/
module main
+62
View File
@@ -0,0 +1,62 @@
// v-hono picoev 服务器示例 - 高性能版本
// 运行: v run server_hono_picoev.v
// 测试: curl http://127.0.0.1:8081/
module main
import hono
import net.http
fn main() {
mut app := hono.Hono.new()
// 静态路由
app.get('/', fn (mut c hono.Context) http.Response {
return c.text('Hello World')
})
app.get('/api/health', fn (mut c hono.Context) http.Response {
return c.text('OK')
})
app.get('/api/users', fn (mut c hono.Context) http.Response {
return c.json('{"users": []}')
})
app.post('/api/users', fn (mut c hono.Context) http.Response {
return c.json('{"created": true}')
})
// 动态路由
app.get('/api/users/:id', fn (mut c hono.Context) http.Response {
id := c.params['id'] or { '' }
return c.json('{"id": "${id}"}')
})
app.get('/api/users/:id/posts', fn (mut c hono.Context) http.Response {
id := c.params['id'] or { '' }
return c.json('{"posts": [], "user_id": "${id}"}')
})
app.get('/api/users/:user_id/posts/:post_id', fn (mut c hono.Context) http.Response {
user_id := c.params['user_id'] or { '' }
post_id := c.params['post_id'] or { '' }
return c.json('{"user_id": "${user_id}", "post_id": "${post_id}"}')
})
app.get('/api/categories/:cat/items/:item', fn (mut c hono.Context) http.Response {
cat := c.params['cat'] or { '' }
item := c.params['item'] or { '' }
return c.json('{"category": "${cat}", "item": "${item}"}')
})
println('╔═══════════════════════════════════════════════════════════════╗')
println('║ v-hono picoev 服务器 - 高性能版本 ║')
println('╠═══════════════════════════════════════════════════════════════╣')
println('║ 端口: 8081 ║')
println('║ 特性: picoev 事件驱动 + Keep-Alive ║')
println('╚═══════════════════════════════════════════════════════════════╝')
// 使用 picoev 启动服务器
app.listen_picoev(8081)
}
+164
View File
@@ -0,0 +1,164 @@
// veb 服务器示例 - 用于性能对比测试
// veb 是 V 语言官方的新版 web 框架(替代旧的 vweb)
//
// 运行: v run server_veb.v
// 测试: curl http://127.0.0.1:8080/
//
// 压测命令:
// wrk -t4 -c100 -d10s http://127.0.0.1:8080/
// wrk -t4 -c100 -d10s http://127.0.0.1:8080/api/users/123
module main
import veb
import time
// 应用状态
pub struct App {
pub:
start_time time.Time = time.now()
}
// Context 类型
pub struct Context {
veb.Context
}
fn main() {
println('╔═══════════════════════════════════════════════════════════════╗')
println('║ veb 服务器 - 性能对比测试 ║')
println('╠═══════════════════════════════════════════════════════════════╣')
println('║ 端口: 8080 ║')
println('║ 测试端点: ║')
println('║ GET / - Hello World ║')
println('║ GET /api/health - 健康检查 ║')
println('║ GET /api/users - 获取用户列表 ║')
println('║ POST /api/users - 创建用户 ║')
println('║ GET /api/users/:id - 获取单个用户 ║')
println('║ GET /api/users/:id/posts - 获取用户帖子 ║')
println('╚═══════════════════════════════════════════════════════════════╝')
println('')
println('启动 veb 服务器在端口 8080...')
mut app := &App{}
veb.run[App, Context](mut app, 8080)
}
// ============================================
// 静态路由
// ============================================
// 首页
@['/']
pub fn (app &App) index(mut ctx Context) veb.Result {
ctx.set_header(.connection, 'close')
return ctx.text('Hello World')
}
// 健康检查
@['/api/health']
pub fn (app &App) health(mut ctx Context) veb.Result {
ctx.set_header(.connection, 'close')
return ctx.text('OK')
}
// 获取用户列表
@['/api/users'; get]
pub fn (app &App) get_users(mut ctx Context) veb.Result {
ctx.set_header(.connection, 'close')
return ctx.json[[]User]([
User{id: '1', name: 'Alice', email: 'alice@example.com'},
User{id: '2', name: 'Bob', email: 'bob@example.com'},
])
}
// 创建用户
@['/api/users'; post]
pub fn (app &App) create_user(mut ctx Context) veb.Result {
ctx.set_header(.connection, 'close')
return ctx.json[CreateResponse](CreateResponse{created: true, id: '3'})
}
// ============================================
// 动态路由
// ============================================
// 获取单个用户
@['/api/users/:id']
pub fn (app &App) get_user(mut ctx Context, id string) veb.Result {
ctx.set_header(.connection, 'close')
return ctx.json[User](User{id: id, name: 'User ${id}', email: 'user${id}@example.com'})
}
// 获取用户的帖子
@['/api/users/:id/posts']
pub fn (app &App) get_user_posts(mut ctx Context, id string) veb.Result {
ctx.set_header(.connection, 'close')
return ctx.json[PostsResponse](PostsResponse{
user_id: id
posts: [
Post{id: '1', title: 'First Post', content: 'Hello World'},
Post{id: '2', title: 'Second Post', content: 'V is awesome'},
]
})
}
// 获取特定帖子
@['/api/users/:user_id/posts/:post_id']
pub fn (app &App) get_user_post(mut ctx Context, user_id string, post_id string) veb.Result {
ctx.set_header(.connection, 'close')
return ctx.json[PostResponse](PostResponse{
user_id: user_id
post_id: post_id
post: Post{id: post_id, title: 'Post ${post_id}', content: 'Content of post ${post_id}'}
})
}
// 获取分类商品
@['/api/categories/:cat/items/:item']
pub fn (app &App) get_category_item(mut ctx Context, cat string, item string) veb.Result {
ctx.set_header(.connection, 'close')
return ctx.json[ItemResponse](ItemResponse{
category: cat
item: item
price: 99.99
})
}
// ============================================
// 数据结构
// ============================================
struct User {
id string
name string
email string
}
struct Post {
id string
title string
content string
}
struct CreateResponse {
created bool
id string
}
struct PostsResponse {
user_id string
posts []Post
}
struct PostResponse {
user_id string
post_id string
post Post
}
struct ItemResponse {
category string
item string
price f64
}
+1 -1
View File
@@ -1,6 +1,6 @@
// vweb 服务器示例 - 用于性能对比测试
// 运行: v run server_vweb.v
// 测试: curl http://localhost:8080/
// 测试: curl http://127.0.0.1:8080/
module main
+93
View File
@@ -0,0 +1,93 @@
// 简单 HTTP 压测 - veb vs v-hono
// v run simple_benchmark.v
module main
import net.http
import time
const requests = 50
fn main() {
println('╔═══════════════════════════════════════════════════════════════╗')
println('║ 简单 HTTP 压测 - veb vs v-hono ║')
println('╚═══════════════════════════════════════════════════════════════╝')
println('')
// 测试 veb
println('测试 veb (http://127.0.0.1:8080/)...')
veb_result := benchmark_server('http://127.0.0.1:8080/')
println(' 结果: ${veb_result.rps:.0} req/s | avg: ${veb_result.avg_ms:.2}ms | 成功: ${veb_result.success}/${veb_result.total}')
// 测试 v-hono
println('')
println('测试 v-hono (http://127.0.0.1:8081/)...')
hono_result := benchmark_server('http://127.0.0.1:8081/')
println(' 结果: ${hono_result.rps:.0} req/s | avg: ${hono_result.avg_ms:.2}ms | 成功: ${hono_result.success}/${hono_result.total}')
// 对比
println('')
println('═══════════════════════════════════════════════════════════════')
if veb_result.rps > 0 && hono_result.rps > 0 {
if hono_result.rps > veb_result.rps {
ratio := hono_result.rps / veb_result.rps
println('v-hono 快 ${ratio:.2}x')
} else {
ratio := veb_result.rps / hono_result.rps
println('veb 快 ${ratio:.2}x')
}
}
println('═══════════════════════════════════════════════════════════════')
}
struct Result {
total int
success int
rps f64
avg_ms f64
}
fn benchmark_server(url string) Result {
// 先检查服务器
http.get(url) or {
println(' ⚠️ 服务器不可用')
return Result{}
}
mut success := 0
mut total_ms := f64(0)
start := time.now()
for i in 0 .. requests {
req_start := time.now()
resp := http.get(url) or {
continue
}
req_ms := f64(time.since(req_start).microseconds()) / 1000.0
if resp.status_code == 200 {
success++
}
total_ms += req_ms
// 进度
if (i + 1) % 10 == 0 {
print('.')
}
}
println('')
elapsed := time.since(start)
elapsed_ms := elapsed.milliseconds()
rps := if elapsed_ms > 0 { f64(requests) * 1000.0 / f64(elapsed_ms) } else { 0.0 }
avg_ms := if requests > 0 { total_ms / f64(requests) } else { 0.0 }
return Result{
total: requests
success: success
rps: rps
avg_ms: avg_ms
}
}
+12 -12
View File
@@ -21,7 +21,7 @@ fn main() {
// 等待服务器启动
time.sleep(2 * time.second)
println('服务器已启动在 http://localhost:8081')
println('服务器已启动在 http://127.0.0.1:8081')
// 执行测试
run_tests()
@@ -124,7 +124,7 @@ fn test_small_file_performance() {
// 传统方式
start := time.now()
for i in 0 .. 100 {
_ = os.execute('curl -s http://localhost:8081/traditional/test_small.txt > nul')
_ = os.execute('curl -s http://127.0.0.1:8081/traditional/test_small.txt > nul')
}
traditional_time := time.now() - start
println('传统方式 100次请求: $traditional_time')
@@ -132,7 +132,7 @@ fn test_small_file_performance() {
// 流式方式
start2 := time.now()
for i in 0 .. 100 {
_ = os.execute('curl -s http://localhost:8081/stream/test_small.txt > nul')
_ = os.execute('curl -s http://127.0.0.1:8081/stream/test_small.txt > nul')
}
stream_time := time.now() - start2
println('流式方式 100次请求: $stream_time')
@@ -140,7 +140,7 @@ fn test_small_file_performance() {
// 智能方式
start3 := time.now()
for i in 0 .. 100 {
_ = os.execute('curl -s http://localhost:8081/smart/test_small.txt > nul')
_ = os.execute('curl -s http://127.0.0.1:8081/smart/test_small.txt > nul')
}
smart_time := time.now() - start3
println('智能方式 100次请求: $smart_time')
@@ -152,7 +152,7 @@ fn test_medium_file_performance() {
// 传统方式
start := time.now()
for i in 0 .. 10 {
_ = os.execute('curl -s http://localhost:8081/traditional/test_medium.txt > nul')
_ = os.execute('curl -s http://127.0.0.1:8081/traditional/test_medium.txt > nul')
}
traditional_time := time.now() - start
println('传统方式 10次请求: $traditional_time')
@@ -160,7 +160,7 @@ fn test_medium_file_performance() {
// 流式方式
start2 := time.now()
for i in 0 .. 10 {
_ = os.execute('curl -s http://localhost:8081/stream/test_medium.txt > nul')
_ = os.execute('curl -s http://127.0.0.1:8081/stream/test_medium.txt > nul')
}
stream_time := time.now() - start2
println('流式方式 10次请求: $stream_time')
@@ -172,7 +172,7 @@ fn test_large_file_performance() {
// 只测试流式方式,避免内存问题
start := time.now()
for i in 0 .. 5 {
result := os.execute('curl -s http://localhost:8081/stream/test_large.txt > nul')
result := os.execute('curl -s http://127.0.0.1:8081/stream/test_large.txt > nul')
if result.exit_code != 0 {
println(' 请求 $i 失败')
}
@@ -183,7 +183,7 @@ fn test_large_file_performance() {
// 智能方式(应该自动选择流式传输)
start2 := time.now()
for i in 0 .. 5 {
result := os.execute('curl -s http://localhost:8081/smart/test_large.txt > nul')
result := os.execute('curl -s http://127.0.0.1:8081/smart/test_large.txt > nul')
if result.exit_code != 0 {
println(' 智能方式请求 $i 失败')
}
@@ -196,14 +196,14 @@ fn test_range_requests() {
println('\n--- Range请求测试 ---')
// 测试部分内容请求
result1 := os.execute('curl -s -H "Range: bytes=0-99" http://localhost:8081/range/test_medium.txt')
result1 := os.execute('curl -s -H "Range: bytes=0-99" http://127.0.0.1:8081/range/test_medium.txt')
if result1.exit_code == 0 {
println('✅ Range请求 0-99: 成功')
} else {
println('❌ Range请求 0-99: 失败')
}
result2 := os.execute('curl -s -H "Range: bytes=100-199" http://localhost:8081/range/test_medium.txt')
result2 := os.execute('curl -s -H "Range: bytes=100-199" http://127.0.0.1:8081/range/test_medium.txt')
if result2.exit_code == 0 {
println('✅ Range请求 100-199: 成功')
} else {
@@ -215,7 +215,7 @@ fn test_smart_selection() {
println('\n--- 智能选择测试 ---')
// 测试小文件(应该使用内存加载)
result1 := os.execute('curl -s http://localhost:8081/smart/test_small.txt')
result1 := os.execute('curl -s http://127.0.0.1:8081/smart/test_small.txt')
if result1.exit_code == 0 {
println('✅ 智能选择小文件: 成功')
} else {
@@ -223,7 +223,7 @@ fn test_smart_selection() {
}
// 测试大文件(应该使用流式传输)
result2 := os.execute('curl -s -o large_output.txt http://localhost:8081/smart/test_large.txt')
result2 := os.execute('curl -s -o large_output.txt http://127.0.0.1:8081/smart/test_large.txt')
if result2.exit_code == 0 {
// 检查下载的文件大小
if os.exists('large_output.txt') {