perf: 0 middleware ignore copy
This commit is contained in:
@@ -40,6 +40,10 @@ pub mut:
|
|||||||
use_fast_router bool = true // 新增:是否使用快速路由器
|
use_fast_router bool = true // 新增:是否使用快速路由器
|
||||||
context_middlewares []ContextMiddleware
|
context_middlewares []ContextMiddleware
|
||||||
route_middlewares map[string][]ContextMiddleware // 路由前缀对应的中间件
|
route_middlewares map[string][]ContextMiddleware // 路由前缀对应的中间件
|
||||||
|
// 优化:预排序的中间件前缀列表(启动时计算一次)
|
||||||
|
sorted_middleware_prefixes []string
|
||||||
|
// 优化:标记是否有中间件(用于零中间件快速路径)
|
||||||
|
has_middlewares bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Context 中间件类型
|
// Context 中间件类型
|
||||||
@@ -185,6 +189,14 @@ pub fn (mut app Hono) all(path string, handler fn (mut Context) http.Response) {
|
|||||||
// Context 中间件
|
// Context 中间件
|
||||||
pub fn (mut app Hono) use(mw ContextMiddleware) {
|
pub fn (mut app Hono) use(mw ContextMiddleware) {
|
||||||
app.context_middlewares << mw
|
app.context_middlewares << mw
|
||||||
|
app.has_middlewares = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 预计算中间件前缀排序(在服务器启动前调用)
|
||||||
|
pub fn (mut app Hono) precompute_middleware_prefixes() {
|
||||||
|
app.sorted_middleware_prefixes = app.route_middlewares.keys()
|
||||||
|
app.sorted_middleware_prefixes.sort(a.len < b.len)
|
||||||
|
app.has_middlewares = app.context_middlewares.len > 0 || app.route_middlewares.len > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// notFound() - 自定义 404 处理器
|
// notFound() - 自定义 404 处理器
|
||||||
@@ -234,12 +246,18 @@ fn (mut s ServerHanler) handle(req http.Request) http.Response {
|
|||||||
body := req.data
|
body := req.data
|
||||||
// 构造 Context
|
// 构造 Context
|
||||||
mut ctx := Context.new(req, param_map, query_map, body)
|
mut ctx := Context.new(req, param_map, query_map, body)
|
||||||
|
|
||||||
|
// 优化:零中间件快速路径
|
||||||
|
if !s.app.has_middlewares {
|
||||||
|
return route_match.handler.handle(mut ctx)
|
||||||
|
}
|
||||||
|
|
||||||
// 获取该路径对应的中间件
|
// 获取该路径对应的中间件
|
||||||
middlewares := s.get_middlewares_for_path(url.path)
|
middlewares := s.get_middlewares_for_path(url.path)
|
||||||
// 洋葱模型递归执行中间件
|
// 洋葱模型递归执行中间件
|
||||||
return s.exec_context_middlewares_with_list(0, middlewares, mut ctx, fn [route_match] (mut c Context) http.Response {
|
return s.exec_context_middlewares_with_list(0, middlewares, mut ctx, fn [route_match] (mut c Context) http.Response {
|
||||||
return route_match.handler.handle(mut c)
|
return route_match.handler.handle(mut c)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果快速路由器没有匹配,回退到混合路由器
|
// 如果快速路由器没有匹配,回退到混合路由器
|
||||||
@@ -250,6 +268,12 @@ fn (mut s ServerHanler) handle(req http.Request) http.Response {
|
|||||||
body := req.data
|
body := req.data
|
||||||
// 构造 Context
|
// 构造 Context
|
||||||
mut ctx := Context.new(req, param_map, query_map, body)
|
mut ctx := Context.new(req, param_map, query_map, body)
|
||||||
|
|
||||||
|
// 优化:零中间件快速路径
|
||||||
|
if !s.app.has_middlewares {
|
||||||
|
return route_match.handler.handle(mut ctx)
|
||||||
|
}
|
||||||
|
|
||||||
// 获取该路径对应的中间件
|
// 获取该路径对应的中间件
|
||||||
middlewares := s.get_middlewares_for_path(url.path)
|
middlewares := s.get_middlewares_for_path(url.path)
|
||||||
// 洋葱模型递归执行中间件
|
// 洋葱模型递归执行中间件
|
||||||
@@ -265,6 +289,12 @@ fn (mut s ServerHanler) handle(req http.Request) http.Response {
|
|||||||
body := req.data
|
body := req.data
|
||||||
// 构造 Context
|
// 构造 Context
|
||||||
mut ctx := Context.new(req, param_map, query_map, body)
|
mut ctx := Context.new(req, param_map, query_map, body)
|
||||||
|
|
||||||
|
// 优化:零中间件快速路径
|
||||||
|
if !s.app.has_middlewares {
|
||||||
|
return route_match.handler.handle(mut ctx)
|
||||||
|
}
|
||||||
|
|
||||||
// 获取该路径对应的中间件
|
// 获取该路径对应的中间件
|
||||||
middlewares := s.get_middlewares_for_path(url.path)
|
middlewares := s.get_middlewares_for_path(url.path)
|
||||||
// 洋葱模型递归执行中间件
|
// 洋葱模型递归执行中间件
|
||||||
@@ -291,15 +321,17 @@ fn (mut s ServerHanler) handle(req http.Request) http.Response {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取路径对应的所有中间件(全局 + 路由前缀匹配的)
|
// 获取路径对应的所有中间件(全局 + 路由前缀匹配的)- 优化版
|
||||||
fn (s ServerHanler) get_middlewares_for_path(path string) []ContextMiddleware {
|
fn (s ServerHanler) get_middlewares_for_path(path string) []ContextMiddleware {
|
||||||
|
// 优化:只有全局中间件时,直接返回引用(避免克隆)
|
||||||
|
if s.app.route_middlewares.len == 0 {
|
||||||
|
return s.app.context_middlewares
|
||||||
|
}
|
||||||
|
|
||||||
mut middlewares := s.app.context_middlewares.clone()
|
mut middlewares := s.app.context_middlewares.clone()
|
||||||
|
|
||||||
// 按前缀长度排序,确保更具体的前缀后执行
|
// 优化:使用预排序的前缀列表(启动时已排序,不需要每次请求都排序)
|
||||||
mut prefixes := s.app.route_middlewares.keys()
|
for prefix in s.app.sorted_middleware_prefixes {
|
||||||
prefixes.sort(a.len < b.len)
|
|
||||||
|
|
||||||
for prefix in prefixes {
|
|
||||||
if path.starts_with(prefix) || prefix == '/' {
|
if path.starts_with(prefix) || prefix == '/' {
|
||||||
if mws := s.app.route_middlewares[prefix] {
|
if mws := s.app.route_middlewares[prefix] {
|
||||||
middlewares << mws
|
middlewares << mws
|
||||||
@@ -342,6 +374,9 @@ pub fn (mut app Hono) listen(port string) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 优化:预计算中间件前缀排序
|
||||||
|
app.precompute_middleware_prefixes()
|
||||||
|
|
||||||
// 使用优化配置的 picoev 高性能服务器(支持高并发)
|
// 使用优化配置的 picoev 高性能服务器(支持高并发)
|
||||||
app.listen_picoev_with_config(PicoevConfig{
|
app.listen_picoev_with_config(PicoevConfig{
|
||||||
port: port_num
|
port: port_num
|
||||||
@@ -372,6 +407,7 @@ pub fn (mut app Hono) route(prefix string, mut subapp Hono) {
|
|||||||
} else {
|
} else {
|
||||||
app.route_middlewares[prefix] = subapp.context_middlewares.clone()
|
app.route_middlewares[prefix] = subapp.context_middlewares.clone()
|
||||||
}
|
}
|
||||||
|
app.has_middlewares = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// 继承子应用的 notFound 和 onError 处理器(如果主应用没有设置)
|
// 继承子应用的 notFound 和 onError 处理器(如果主应用没有设置)
|
||||||
@@ -508,5 +544,7 @@ pub fn Hono.new() Hono {
|
|||||||
fast_router: FastRouter.new()
|
fast_router: FastRouter.new()
|
||||||
use_fast_router: true
|
use_fast_router: true
|
||||||
route_middlewares: map[string][]ContextMiddleware{}
|
route_middlewares: map[string][]ContextMiddleware{}
|
||||||
|
sorted_middleware_prefixes: []string{}
|
||||||
|
has_middlewares: false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-6
@@ -171,17 +171,18 @@ fn (router FastRouter) precompile_route(method string, handler IHandler) !Precom
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 快速路由匹配(优化版:静态路由跳过缓存 + 预分配结果)
|
// 快速路由匹配(优化版:静态路由跳过缓存 + 预分配结果 + 复用 cache key)
|
||||||
pub fn (mut router FastRouter) match_route(method string, path string) ?ContextRouteMatch {
|
pub fn (mut router FastRouter) match_route(method string, path string) ?ContextRouteMatch {
|
||||||
|
// 优化:只拼接一次 cache key,复用于静态路由和缓存查找
|
||||||
|
cache_key := '${method}:${path}'
|
||||||
|
|
||||||
// 1. 静态路由直接返回预分配的结果(零分配)
|
// 1. 静态路由直接返回预分配的结果(零分配)
|
||||||
static_key := '${method}:${path}'
|
if result := router.static_route_results[cache_key] {
|
||||||
if result := router.static_route_results[static_key] {
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. 动态路由先检查 LRU 缓存(避免重复正则匹配)
|
// 2. 动态路由先检查 LRU 缓存(避免重复正则匹配)
|
||||||
if router.cache_enabled {
|
if router.cache_enabled {
|
||||||
cache_key := '${method}:${path}'
|
|
||||||
if cached := router.lru_cache.get(cache_key) {
|
if cached := router.lru_cache.get(cache_key) {
|
||||||
return cached
|
return cached
|
||||||
}
|
}
|
||||||
@@ -208,9 +209,8 @@ pub fn (mut router FastRouter) match_route(method string, path string) ?ContextR
|
|||||||
base_path: ''
|
base_path: ''
|
||||||
}
|
}
|
||||||
|
|
||||||
// 只缓存动态路由结果
|
// 只缓存动态路由结果(复用 cache_key)
|
||||||
if router.cache_enabled {
|
if router.cache_enabled {
|
||||||
cache_key := '${method}:${path}'
|
|
||||||
router.lru_cache.put(cache_key, result)
|
router.lru_cache.put(cache_key, result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+32
-8
@@ -79,7 +79,15 @@ fn picoev_callback(user_data voidptr, req picohttpparser.Request, mut res picoht
|
|||||||
if ctx.app.use_fast_router {
|
if ctx.app.use_fast_router {
|
||||||
if route_match := ctx.app.fast_router.match_route(method_str, path) {
|
if route_match := ctx.app.fast_router.match_route(method_str, path) {
|
||||||
mut hono_ctx := create_picoev_context(req, route_match.params, query_map)
|
mut hono_ctx := create_picoev_context(req, route_match.params, query_map)
|
||||||
middlewares := get_middlewares_for_path_picoev(ctx.app, path)
|
|
||||||
|
// 优化:零中间件快速路径
|
||||||
|
if !ctx.app.has_middlewares {
|
||||||
|
response := route_match.handler.handle(mut hono_ctx)
|
||||||
|
send_picoev_response(mut res, hono_ctx, response, keepalive, ctx.config)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
middlewares := get_middlewares_for_path_picoev_optimized(ctx.app, path)
|
||||||
response := exec_middlewares_picoev(0, middlewares, mut hono_ctx, route_match.handler)
|
response := exec_middlewares_picoev(0, middlewares, mut hono_ctx, route_match.handler)
|
||||||
send_picoev_response(mut res, hono_ctx, response, keepalive, ctx.config)
|
send_picoev_response(mut res, hono_ctx, response, keepalive, ctx.config)
|
||||||
return
|
return
|
||||||
@@ -89,7 +97,15 @@ fn picoev_callback(user_data voidptr, req picohttpparser.Request, mut res picoht
|
|||||||
// 回退到混合路由器
|
// 回退到混合路由器
|
||||||
if route_match := ctx.app.context_hybrid_router.match_route(method_str, path) {
|
if route_match := ctx.app.context_hybrid_router.match_route(method_str, path) {
|
||||||
mut hono_ctx := create_picoev_context(req, route_match.params, query_map)
|
mut hono_ctx := create_picoev_context(req, route_match.params, query_map)
|
||||||
middlewares := get_middlewares_for_path_picoev(ctx.app, path)
|
|
||||||
|
// 优化:零中间件快速路径
|
||||||
|
if !ctx.app.has_middlewares {
|
||||||
|
response := route_match.handler.handle(mut hono_ctx)
|
||||||
|
send_picoev_response(mut res, hono_ctx, response, keepalive, ctx.config)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
middlewares := get_middlewares_for_path_picoev_optimized(ctx.app, path)
|
||||||
response := exec_middlewares_picoev(0, middlewares, mut hono_ctx, route_match.handler)
|
response := exec_middlewares_picoev(0, middlewares, mut hono_ctx, route_match.handler)
|
||||||
send_picoev_response(mut res, hono_ctx, response, keepalive, ctx.config)
|
send_picoev_response(mut res, hono_ctx, response, keepalive, ctx.config)
|
||||||
return
|
return
|
||||||
@@ -129,14 +145,17 @@ fn check_keepalive_request(req picohttpparser.Request) bool {
|
|||||||
return true // HTTP/1.1 默认 Keep-Alive
|
return true // HTTP/1.1 默认 Keep-Alive
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取路径对应的所有中间件
|
// 获取路径对应的所有中间件(优化版:使用预排序的前缀列表)
|
||||||
fn get_middlewares_for_path_picoev(app &Hono, path string) []ContextMiddleware {
|
fn get_middlewares_for_path_picoev_optimized(app &Hono, path string) []ContextMiddleware {
|
||||||
|
// 优化:只有全局中间件时,直接返回引用(避免克隆)
|
||||||
|
if app.route_middlewares.len == 0 {
|
||||||
|
return app.context_middlewares
|
||||||
|
}
|
||||||
|
|
||||||
mut middlewares := app.context_middlewares.clone()
|
mut middlewares := app.context_middlewares.clone()
|
||||||
|
|
||||||
mut prefixes := app.route_middlewares.keys()
|
// 优化:使用预排序的前缀列表(启动时已排序,不需要每次请求都排序)
|
||||||
prefixes.sort(a.len < b.len)
|
for prefix in app.sorted_middleware_prefixes {
|
||||||
|
|
||||||
for prefix in prefixes {
|
|
||||||
if path.starts_with(prefix) || prefix == '/' {
|
if path.starts_with(prefix) || prefix == '/' {
|
||||||
if mws := app.route_middlewares[prefix] {
|
if mws := app.route_middlewares[prefix] {
|
||||||
middlewares << mws
|
middlewares << mws
|
||||||
@@ -147,6 +166,11 @@ fn get_middlewares_for_path_picoev(app &Hono, path string) []ContextMiddleware {
|
|||||||
return middlewares
|
return middlewares
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取路径对应的所有中间件(保留旧版本兼容)
|
||||||
|
fn get_middlewares_for_path_picoev(app &Hono, path string) []ContextMiddleware {
|
||||||
|
return get_middlewares_for_path_picoev_optimized(app, path)
|
||||||
|
}
|
||||||
|
|
||||||
// 执行中间件链
|
// 执行中间件链
|
||||||
fn exec_middlewares_picoev(idx int, middlewares []ContextMiddleware, mut ctx Context, handler IHandler) http.Response {
|
fn exec_middlewares_picoev(idx int, middlewares []ContextMiddleware, mut ctx Context, handler IHandler) http.Response {
|
||||||
if idx < middlewares.len {
|
if idx < middlewares.len {
|
||||||
|
|||||||
@@ -208,10 +208,34 @@ pub fn (router ContextHybridRouter) match_static_route(method string, path strin
|
|||||||
return none
|
return none
|
||||||
}
|
}
|
||||||
|
|
||||||
// Context 版本的动态路径匹配
|
// Context 版本的动态路径匹配(保留旧版本兼容)
|
||||||
fn (mut router ContextHybridRouter) match_dynamic_route(method string, path string) ?ContextRouteMatch {
|
fn (mut router ContextHybridRouter) match_dynamic_route(method string, path string) ?ContextRouteMatch {
|
||||||
// 先检查缓存
|
|
||||||
cache_key := '${method}:${path}'
|
cache_key := '${method}:${path}'
|
||||||
|
return router.match_dynamic_route_with_key(cache_key, method, path)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Context 版本的主匹配函数(优化版:复用 cache key)
|
||||||
|
pub fn (mut router ContextHybridRouter) match_route(method string, path string) ?ContextRouteMatch {
|
||||||
|
// 优化:只拼接一次 cache key
|
||||||
|
cache_key := '${method}:${path}'
|
||||||
|
|
||||||
|
// 1. 先尝试静态路径匹配(最快)
|
||||||
|
if cache_key in router.static_routes {
|
||||||
|
return ContextRouteMatch{
|
||||||
|
handler: router.static_routes[cache_key]
|
||||||
|
params: map[string]string{}
|
||||||
|
path: path
|
||||||
|
base_path: ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 再尝试动态路径匹配(复用 cache_key)
|
||||||
|
return router.match_dynamic_route_with_key(cache_key, method, path)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Context 版本的动态路径匹配(优化版:接收预计算的 cache_key)
|
||||||
|
fn (mut router ContextHybridRouter) match_dynamic_route_with_key(cache_key string, method string, path string) ?ContextRouteMatch {
|
||||||
|
// 先检查缓存
|
||||||
if cached := router.cache.get(cache_key) {
|
if cached := router.cache.get(cache_key) {
|
||||||
return cached
|
return cached
|
||||||
}
|
}
|
||||||
@@ -240,22 +264,6 @@ fn (mut router ContextHybridRouter) match_dynamic_route(method string, path stri
|
|||||||
return none
|
return none
|
||||||
}
|
}
|
||||||
|
|
||||||
// Context 版本的主匹配函数
|
|
||||||
pub fn (mut router ContextHybridRouter) match_route(method string, path string) ?ContextRouteMatch {
|
|
||||||
// 1. 先尝试静态路径匹配(最快)
|
|
||||||
if static_handler := router.match_static_route(method, path) {
|
|
||||||
return ContextRouteMatch{
|
|
||||||
handler: static_handler
|
|
||||||
params: map[string]string{}
|
|
||||||
path: path
|
|
||||||
base_path: ''
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. 再尝试动态路径匹配
|
|
||||||
return router.match_dynamic_route(method, path)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Context 版本的获取所有路由
|
// Context 版本的获取所有路由
|
||||||
pub fn (router ContextHybridRouter) get_all_routes() ([]string, []string) {
|
pub fn (router ContextHybridRouter) get_all_routes() ([]string, []string) {
|
||||||
mut static_paths := []string{}
|
mut static_paths := []string{}
|
||||||
|
|||||||
@@ -0,0 +1,355 @@
|
|||||||
|
// 中间件优化测试
|
||||||
|
// 测试三个优化点:
|
||||||
|
// 1. 零中间件快速路径
|
||||||
|
// 2. 中间件预计算(启动时排序)
|
||||||
|
// 3. 复用 cache key
|
||||||
|
|
||||||
|
import hono
|
||||||
|
import time
|
||||||
|
import net.http
|
||||||
|
|
||||||
|
// 测试统计
|
||||||
|
struct TestStats {
|
||||||
|
mut:
|
||||||
|
total_tests int
|
||||||
|
passed_tests int
|
||||||
|
failed_tests int
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut stats TestStats) start_test(test_name string) {
|
||||||
|
stats.total_tests++
|
||||||
|
print('🧪 ${test_name}... ')
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut stats TestStats) pass_test() {
|
||||||
|
stats.passed_tests++
|
||||||
|
println('✅')
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut stats TestStats) fail_test(error string) {
|
||||||
|
stats.failed_tests++
|
||||||
|
println('❌ ${error}')
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (stats TestStats) print_summary() {
|
||||||
|
println('\n=== 测试总结 ===')
|
||||||
|
println('总测试数: ${stats.total_tests}')
|
||||||
|
println('通过: ${stats.passed_tests}')
|
||||||
|
println('失败: ${stats.failed_tests}')
|
||||||
|
if stats.failed_tests == 0 {
|
||||||
|
println('🎉 所有测试通过!')
|
||||||
|
} else {
|
||||||
|
println('⚠️ 有 ${stats.failed_tests} 个测试失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试1:零中间件快速路径 - has_middlewares 标志
|
||||||
|
fn test_zero_middleware_flag(mut stats TestStats) {
|
||||||
|
stats.start_test('零中间件标志 (has_middlewares)')
|
||||||
|
|
||||||
|
mut app := hono.Hono.new()
|
||||||
|
|
||||||
|
// 新创建的应用应该没有中间件
|
||||||
|
if app.has_middlewares {
|
||||||
|
stats.fail_test('新应用不应该有中间件标志')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加中间件后标志应该为 true
|
||||||
|
app.use(fn (mut c hono.Context, next fn (mut hono.Context) http.Response) http.Response {
|
||||||
|
return next(mut c)
|
||||||
|
})
|
||||||
|
|
||||||
|
if !app.has_middlewares {
|
||||||
|
stats.fail_test('添加中间件后标志应该为 true')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
stats.pass_test()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试2:中间件预计算 - sorted_middleware_prefixes
|
||||||
|
fn test_middleware_precompute(mut stats TestStats) {
|
||||||
|
stats.start_test('中间件预计算 (sorted_middleware_prefixes)')
|
||||||
|
|
||||||
|
mut app := hono.Hono.new()
|
||||||
|
|
||||||
|
// 添加路由
|
||||||
|
app.get('/test', fn (mut c hono.Context) http.Response {
|
||||||
|
return c.text('test')
|
||||||
|
})
|
||||||
|
|
||||||
|
// 创建子应用并添加中间件
|
||||||
|
mut sub_app := hono.Hono.new()
|
||||||
|
sub_app.use(fn (mut c hono.Context, next fn (mut hono.Context) http.Response) http.Response {
|
||||||
|
return next(mut c)
|
||||||
|
})
|
||||||
|
sub_app.get('/hello', fn (mut c hono.Context) http.Response {
|
||||||
|
return c.text('hello')
|
||||||
|
})
|
||||||
|
|
||||||
|
// 挂载子应用
|
||||||
|
app.route('/api', mut sub_app)
|
||||||
|
|
||||||
|
// 预计算前,sorted_middleware_prefixes 应该为空
|
||||||
|
if app.sorted_middleware_prefixes.len != 0 {
|
||||||
|
stats.fail_test('预计算前 sorted_middleware_prefixes 应该为空')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 调用预计算
|
||||||
|
app.precompute_middleware_prefixes()
|
||||||
|
|
||||||
|
// 预计算后,sorted_middleware_prefixes 应该包含前缀
|
||||||
|
if app.sorted_middleware_prefixes.len == 0 && app.route_middlewares.len > 0 {
|
||||||
|
stats.fail_test('预计算后 sorted_middleware_prefixes 应该包含前缀')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证 has_middlewares 标志
|
||||||
|
if app.route_middlewares.len > 0 && !app.has_middlewares {
|
||||||
|
stats.fail_test('有路由中间件时 has_middlewares 应该为 true')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
stats.pass_test()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试3:FastRouter cache key 复用
|
||||||
|
fn test_fast_router_cache_key_reuse(mut stats TestStats) {
|
||||||
|
stats.start_test('FastRouter cache key 复用')
|
||||||
|
|
||||||
|
mut router := hono.FastRouter.new()
|
||||||
|
|
||||||
|
// 添加静态路由
|
||||||
|
static_handler := hono.ContextHandler{
|
||||||
|
path: '/users'
|
||||||
|
handler: fn (mut c hono.Context) http.Response {
|
||||||
|
return c.text('users')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
router.add_route('GET', static_handler, '') or {
|
||||||
|
stats.fail_test('添加静态路由失败')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加动态路由
|
||||||
|
dynamic_handler := hono.ContextHandler{
|
||||||
|
path: '/users/:id'
|
||||||
|
handler: fn (mut c hono.Context) http.Response {
|
||||||
|
return c.text('user')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
router.add_route('GET', dynamic_handler, '') or {
|
||||||
|
stats.fail_test('添加动态路由失败')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试静态路由匹配
|
||||||
|
if _ := router.match_route('GET', '/users') {
|
||||||
|
// 成功
|
||||||
|
} else {
|
||||||
|
stats.fail_test('静态路由匹配失败')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试动态路由匹配(第一次,会缓存)
|
||||||
|
if match1 := router.match_route('GET', '/users/123') {
|
||||||
|
if match1.params['id'] != '123' {
|
||||||
|
stats.fail_test('参数提取失败')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
stats.fail_test('动态路由匹配失败')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试动态路由匹配(第二次,应该命中缓存)
|
||||||
|
if match2 := router.match_route('GET', '/users/123') {
|
||||||
|
if match2.params['id'] != '123' {
|
||||||
|
stats.fail_test('缓存命中后参数提取失败')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
stats.fail_test('缓存命中失败')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证缓存状态
|
||||||
|
_, _, cache_count := router.get_stats()
|
||||||
|
if cache_count == 0 {
|
||||||
|
stats.fail_test('缓存应该有条目')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
stats.pass_test()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试4:HybridRouter cache key 复用
|
||||||
|
fn test_hybrid_router_cache_key_reuse(mut stats TestStats) {
|
||||||
|
stats.start_test('HybridRouter cache key 复用')
|
||||||
|
|
||||||
|
mut router := hono.ContextHybridRouter.new()
|
||||||
|
|
||||||
|
// 添加动态路由
|
||||||
|
dynamic_handler := hono.ContextHandler{
|
||||||
|
path: '/posts/:id'
|
||||||
|
handler: fn (mut c hono.Context) http.Response {
|
||||||
|
return c.text('post')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
router.add_route('GET', dynamic_handler, '')
|
||||||
|
|
||||||
|
// 第一次匹配(无缓存)
|
||||||
|
if match1 := router.match_route('GET', '/posts/456') {
|
||||||
|
if match1.params['id'] != '456' {
|
||||||
|
stats.fail_test('参数提取失败')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
stats.fail_test('动态路由匹配失败')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 第二次匹配(应该命中缓存)
|
||||||
|
if match2 := router.match_route('GET', '/posts/456') {
|
||||||
|
if match2.params['id'] != '456' {
|
||||||
|
stats.fail_test('缓存命中后参数提取失败')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
stats.fail_test('缓存命中失败')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
stats.pass_test()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试5:性能对比 - 有中间件 vs 无中间件
|
||||||
|
fn test_middleware_performance_comparison(mut stats TestStats) {
|
||||||
|
stats.start_test('中间件性能对比')
|
||||||
|
|
||||||
|
// 创建无中间件的路由器
|
||||||
|
mut router_no_mw := hono.FastRouter.new()
|
||||||
|
handler := hono.ContextHandler{
|
||||||
|
path: '/api/users/:id'
|
||||||
|
handler: fn (mut c hono.Context) http.Response {
|
||||||
|
return c.text('user')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
router_no_mw.add_route('GET', handler, '') or {
|
||||||
|
stats.fail_test('添加路由失败')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 预热
|
||||||
|
for _ in 0 .. 100 {
|
||||||
|
_ := router_no_mw.match_route('GET', '/api/users/123') or { continue }
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试无中间件性能
|
||||||
|
iterations := 10000
|
||||||
|
start_no_mw := time.now()
|
||||||
|
for _ in 0 .. iterations {
|
||||||
|
_ := router_no_mw.match_route('GET', '/api/users/123') or { continue }
|
||||||
|
}
|
||||||
|
duration_no_mw := time.since(start_no_mw)
|
||||||
|
|
||||||
|
// 创建有中间件的应用
|
||||||
|
mut app_with_mw := hono.Hono.new()
|
||||||
|
app_with_mw.use(fn (mut c hono.Context, next fn (mut hono.Context) http.Response) http.Response {
|
||||||
|
return next(mut c)
|
||||||
|
})
|
||||||
|
app_with_mw.get('/api/users/:id', fn (mut c hono.Context) http.Response {
|
||||||
|
return c.text('user')
|
||||||
|
})
|
||||||
|
app_with_mw.precompute_middleware_prefixes()
|
||||||
|
|
||||||
|
// 预热
|
||||||
|
for _ in 0 .. 100 {
|
||||||
|
_ := app_with_mw.fast_router.match_route('GET', '/api/users/123') or { continue }
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试有中间件性能
|
||||||
|
start_with_mw := time.now()
|
||||||
|
for _ in 0 .. iterations {
|
||||||
|
_ := app_with_mw.fast_router.match_route('GET', '/api/users/123') or { continue }
|
||||||
|
}
|
||||||
|
duration_with_mw := time.since(start_with_mw)
|
||||||
|
|
||||||
|
println('')
|
||||||
|
println(' 📊 无中间件: ${duration_no_mw.milliseconds()}ms (${iterations} 次)')
|
||||||
|
println(' 📊 有中间件: ${duration_with_mw.milliseconds()}ms (${iterations} 次)')
|
||||||
|
|
||||||
|
// 性能差异不应该太大(路由匹配本身不受中间件影响)
|
||||||
|
stats.pass_test()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试6:预计算排序正确性
|
||||||
|
fn test_precompute_sorting(mut stats TestStats) {
|
||||||
|
stats.start_test('预计算排序正确性')
|
||||||
|
|
||||||
|
mut app := hono.Hono.new()
|
||||||
|
|
||||||
|
// 添加多个子应用,模拟不同长度的前缀
|
||||||
|
mut sub1 := hono.Hono.new()
|
||||||
|
sub1.use(fn (mut c hono.Context, next fn (mut hono.Context) http.Response) http.Response {
|
||||||
|
return next(mut c)
|
||||||
|
})
|
||||||
|
sub1.get('/test', fn (mut c hono.Context) http.Response {
|
||||||
|
return c.text('test')
|
||||||
|
})
|
||||||
|
|
||||||
|
mut sub2 := hono.Hono.new()
|
||||||
|
sub2.use(fn (mut c hono.Context, next fn (mut hono.Context) http.Response) http.Response {
|
||||||
|
return next(mut c)
|
||||||
|
})
|
||||||
|
sub2.get('/test', fn (mut c hono.Context) http.Response {
|
||||||
|
return c.text('test')
|
||||||
|
})
|
||||||
|
|
||||||
|
mut sub3 := hono.Hono.new()
|
||||||
|
sub3.use(fn (mut c hono.Context, next fn (mut hono.Context) http.Response) http.Response {
|
||||||
|
return next(mut c)
|
||||||
|
})
|
||||||
|
sub3.get('/test', fn (mut c hono.Context) http.Response {
|
||||||
|
return c.text('test')
|
||||||
|
})
|
||||||
|
|
||||||
|
// 按不同顺序挂载(长前缀先挂载)
|
||||||
|
app.route('/api/v1/users', mut sub1)
|
||||||
|
app.route('/api', mut sub2)
|
||||||
|
app.route('/api/v1', mut sub3)
|
||||||
|
|
||||||
|
// 预计算
|
||||||
|
app.precompute_middleware_prefixes()
|
||||||
|
|
||||||
|
// 验证排序:短前缀应该在前面
|
||||||
|
if app.sorted_middleware_prefixes.len >= 2 {
|
||||||
|
for i in 0 .. app.sorted_middleware_prefixes.len - 1 {
|
||||||
|
if app.sorted_middleware_prefixes[i].len > app.sorted_middleware_prefixes[i + 1].len {
|
||||||
|
stats.fail_test('前缀排序不正确:${app.sorted_middleware_prefixes[i]} 应该在 ${app.sorted_middleware_prefixes[i + 1]} 之后')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stats.pass_test()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
println('🚀 开始中间件优化测试...\n')
|
||||||
|
|
||||||
|
mut stats := TestStats{}
|
||||||
|
|
||||||
|
// 运行所有测试
|
||||||
|
test_zero_middleware_flag(mut stats)
|
||||||
|
test_middleware_precompute(mut stats)
|
||||||
|
test_fast_router_cache_key_reuse(mut stats)
|
||||||
|
test_hybrid_router_cache_key_reuse(mut stats)
|
||||||
|
test_middleware_performance_comparison(mut stats)
|
||||||
|
test_precompute_sorting(mut stats)
|
||||||
|
|
||||||
|
// 打印测试总结
|
||||||
|
stats.print_summary()
|
||||||
|
}
|
||||||
+37
-12
@@ -49,6 +49,9 @@ pub fn (mut app Hono) listen_usockets(port int) {
|
|||||||
|
|
||||||
// 使用 uSockets 启动服务器(带配置)
|
// 使用 uSockets 启动服务器(带配置)
|
||||||
pub fn (mut app Hono) listen_usockets_with_config(config UsocketsConfig) {
|
pub fn (mut app Hono) listen_usockets_with_config(config UsocketsConfig) {
|
||||||
|
// 优化:预计算中间件前缀排序
|
||||||
|
app.precompute_middleware_prefixes()
|
||||||
|
|
||||||
// 存储到全局变量
|
// 存储到全局变量
|
||||||
g_usockets_app = unsafe { &app }
|
g_usockets_app = unsafe { &app }
|
||||||
g_usockets_config = config
|
g_usockets_config = config
|
||||||
@@ -102,9 +105,16 @@ fn usockets_on_data(s usockets.Socket, data &char, length int) usockets.Socket {
|
|||||||
if g_usockets_app.use_fast_router {
|
if g_usockets_app.use_fast_router {
|
||||||
if route_match := g_usockets_app.fast_router.match_route(method, path) {
|
if route_match := g_usockets_app.fast_router.match_route(method, path) {
|
||||||
mut hono_ctx := create_usockets_context(method, path, route_match.params, query_map, body)
|
mut hono_ctx := create_usockets_context(method, path, route_match.params, query_map, body)
|
||||||
middlewares := get_middlewares_for_path_usockets(g_usockets_app, path)
|
|
||||||
response := exec_middlewares_usockets(0, middlewares, mut hono_ctx, route_match.handler)
|
// 优化1:零中间件快速路径
|
||||||
send_usockets_response(s, hono_ctx, response)
|
if !g_usockets_app.has_middlewares {
|
||||||
|
response := route_match.handler.handle(mut hono_ctx)
|
||||||
|
send_usockets_response(s, hono_ctx, response)
|
||||||
|
} else {
|
||||||
|
middlewares := get_middlewares_for_path_usockets_optimized(g_usockets_app, path)
|
||||||
|
response := exec_middlewares_usockets(0, middlewares, mut hono_ctx, route_match.handler)
|
||||||
|
send_usockets_response(s, hono_ctx, response)
|
||||||
|
}
|
||||||
response_sent = true
|
response_sent = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -113,9 +123,16 @@ fn usockets_on_data(s usockets.Socket, data &char, length int) usockets.Socket {
|
|||||||
if !response_sent {
|
if !response_sent {
|
||||||
if route_match := g_usockets_app.context_hybrid_router.match_route(method, path) {
|
if route_match := g_usockets_app.context_hybrid_router.match_route(method, path) {
|
||||||
mut hono_ctx := create_usockets_context(method, path, route_match.params, query_map, body)
|
mut hono_ctx := create_usockets_context(method, path, route_match.params, query_map, body)
|
||||||
middlewares := get_middlewares_for_path_usockets(g_usockets_app, path)
|
|
||||||
response := exec_middlewares_usockets(0, middlewares, mut hono_ctx, route_match.handler)
|
// 优化1:零中间件快速路径
|
||||||
send_usockets_response(s, hono_ctx, response)
|
if !g_usockets_app.has_middlewares {
|
||||||
|
response := route_match.handler.handle(mut hono_ctx)
|
||||||
|
send_usockets_response(s, hono_ctx, response)
|
||||||
|
} else {
|
||||||
|
middlewares := get_middlewares_for_path_usockets_optimized(g_usockets_app, path)
|
||||||
|
response := exec_middlewares_usockets(0, middlewares, mut hono_ctx, route_match.handler)
|
||||||
|
send_usockets_response(s, hono_ctx, response)
|
||||||
|
}
|
||||||
response_sent = true
|
response_sent = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -216,14 +233,17 @@ fn create_usockets_context(method string, path string, params map[string]string,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取路径对应的所有中间件
|
// 获取路径对应的所有中间件(优化版:使用预排序的前缀列表)
|
||||||
fn get_middlewares_for_path_usockets(app &Hono, path string) []ContextMiddleware {
|
fn get_middlewares_for_path_usockets_optimized(app &Hono, path string) []ContextMiddleware {
|
||||||
|
// 优化2:只有全局中间件时,直接返回引用(避免克隆)
|
||||||
|
if app.route_middlewares.len == 0 {
|
||||||
|
return app.context_middlewares
|
||||||
|
}
|
||||||
|
|
||||||
mut middlewares := app.context_middlewares.clone()
|
mut middlewares := app.context_middlewares.clone()
|
||||||
|
|
||||||
mut prefixes := app.route_middlewares.keys()
|
// 优化3:使用预排序的前缀列表(启动时已排序,不需要每次请求都排序)
|
||||||
prefixes.sort(a.len < b.len)
|
for prefix in app.sorted_middleware_prefixes {
|
||||||
|
|
||||||
for prefix in prefixes {
|
|
||||||
if path.starts_with(prefix) || prefix == '/' {
|
if path.starts_with(prefix) || prefix == '/' {
|
||||||
if mws := app.route_middlewares[prefix] {
|
if mws := app.route_middlewares[prefix] {
|
||||||
middlewares << mws
|
middlewares << mws
|
||||||
@@ -234,6 +254,11 @@ fn get_middlewares_for_path_usockets(app &Hono, path string) []ContextMiddleware
|
|||||||
return middlewares
|
return middlewares
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取路径对应的所有中间件(保留旧版本兼容)
|
||||||
|
fn get_middlewares_for_path_usockets(app &Hono, path string) []ContextMiddleware {
|
||||||
|
return get_middlewares_for_path_usockets_optimized(app, path)
|
||||||
|
}
|
||||||
|
|
||||||
// 执行中间件链
|
// 执行中间件链
|
||||||
fn exec_middlewares_usockets(idx int, middlewares []ContextMiddleware, mut ctx Context, handler IHandler) http.Response {
|
fn exec_middlewares_usockets(idx int, middlewares []ContextMiddleware, mut ctx Context, handler IHandler) http.Response {
|
||||||
if idx < middlewares.len {
|
if idx < middlewares.len {
|
||||||
|
|||||||
Reference in New Issue
Block a user