From e0a1329426b7e4e760a2a082292c9d7afc2c2ad3 Mon Sep 17 00:00:00 2001 From: AI Engineer Date: Wed, 13 May 2026 00:49:29 +0800 Subject: [PATCH] feat: bump v2.0.1, add CLI help usage, support fast wildcard path matching in README and tests (by AI) --- README.md | 19 ++++++++++++++++++- app_test.go | 20 ++++++++++++++++++++ main.go | 19 ++++++++++++++++++- 3 files changed, 56 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index db4a868..6caad2f 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,24 @@ - **服务发现原生集成**:与 `@go/discover` 无缝集成,支持基于服务名的直连与负载均衡。 - **生命周期接管**:由 `@go/starter` 接管,支持通过 `kill -SIGHUP` 触发本地全量重新同步。 -## 配置模型 (Redis Hash) +## 路由匹配规则 (无需正则的高性能模式) + +网关底层针对高并发场景深度优化,默认支持 O(1) 级别的哈希匹配和高性能前缀匹配,**极大程度地避免了正则表达式的性能开销**。 + +### 1. Host 域名自适应降级匹配 +网关在匹配请求的 Host 时,遵循一套人性化的自动降级回退机制,确保最灵活的配置: +1. **精确域名+端口**: `aa.com:8080` (最高优先级) +2. **纯域名提取**: `aa.com` (能同时适配 `aa.com:8080` 和 `aa.com:80`) +3. **纯端口截获**: `:80` 或 `:8080` (匹配任意该端口的主机) +4. **全局通配符**: `*` 或空字符串 `""` (兜底拦截所有未匹配请求) + +### 2. Path 路径极速前缀匹配 +默认情况下,非正则路径是**精确绝对匹配**。为了取代高消耗的前缀正则,网关特别引入了 `/*` 高性能通配符: +- **精确匹配 (默认)**: `"Path": "/api"` 仅匹配完全等于 `/api` 的请求。 +- **极速前缀匹配 (带 `/*`)**: `"Path": "/api/*"` 将匹配任何以 `/api/` 开头的请求,底层直接使用原生 `strings.HasPrefix` 剥离正则损耗。 + - *路径截取映射*: 当配置 `{"Path": "/old/*", "ToPath": "/new/*"}` 时,请求 `/old/user/1` 会被极速映射为 `/new/user/1`。 + +*注:只有当匹配规则需要提取复杂的动态参数(如中间段 `$1` 捕获)时,才建议使用正则匹配 `^/api/([a-z]+)/xxx`。* Gateway 将配置存储在 Redis 的 Hash 结构中,并按照 **Host (域名)** 进行字段隔离,提升拉取效率和管理粒度。 diff --git a/app_test.go b/app_test.go index 8d6bce2..d79e3cf 100644 --- a/app_test.go +++ b/app_test.go @@ -35,6 +35,9 @@ func TestGateway(t *testing.T) { service.Host("*").GET("/hello", func(req *service.Request) string { return "hello from backend, path: " + req.RequestURI }) + service.Host("*").GET("/hello/world", func(req *service.Request) string { + return "hello from backend, path: " + req.RequestURI + }) asBackend := service.AsyncStart() defer asBackend.Stop() @@ -48,6 +51,7 @@ func TestGateway(t *testing.T) { proxyRules := []service.ProxyRule{ {Path: "^/api/(.*)$", ToApp: "test-backend", ToPath: "/$1"}, {Path: "/direct", ToApp: "test-backend", ToPath: "/hello"}, + {Path: "/v2/*", ToApp: "test-backend", ToPath: "/hello/*"}, } rewriteRules := []service.RewriteRule{ {Path: "^/old-api/(.*)$", ToPath: "/api/$1"}, @@ -130,6 +134,22 @@ func TestGateway(t *testing.T) { t.Fatalf("Proxy regexp failed, got: %s", body) } + // ============================================ + // 测试 2.1: 极速通配符前缀匹配 (Discover) + // ============================================ + req, _ = gohttp.NewRequest("GET", "http://127.0.0.1:"+gwPort+"/v2/world?x=2", nil) + req.Host = "gw.test" + res, err = client.Do(req) + body = "" + if err == nil && res != nil { + b, _ := io.ReadAll(res.Body) + res.Body.Close() + body = string(b) + } + if err != nil || !strings.Contains(body, "hello from backend, path: /hello/world?x=2") { + t.Fatalf("Proxy wildcard failed, got: %s", body) + } + // ============================================ // 测试 3: Rewrite 后再 Proxy // ============================================ diff --git a/main.go b/main.go index 5a5e35c..2f58275 100644 --- a/main.go +++ b/main.go @@ -12,7 +12,24 @@ func main() { _ = config.Load(&GatewayConf, "gateway") // 2. 初始化 Starter 信息 - starter.SetAppInfo("gateway", "2.0.0") + starter.SetAppInfo("gateway", "2.0.1") + starter.SetUsage(`High-performance, event-driven dynamic API gateway. + +Configuration: + Uses env.yml or environment variables for base configuration. + Supports Nginx-like static routing via Proxy/Rewrite/Static declarations in yaml. + Supports real-time atomic hot-reloading via Redis Pub/Sub. + +Routing Path Matching: + - Exact Match (Default): Path: "/api" matches EXACTLY "/api" + - Fast Prefix Match: Path: "/api/*" matches any path starting with "/api/" + - Regex Match: Path: "^/api/(.*)$" captures groups for replacement in ToPath + +Host Matching (Fallback Order): + 1. Exact Host+Port (e.g., "aa.com:8080") + 2. Host Only (e.g., "aa.com") + 3. Port Only (e.g., ":8080") + 4. Global Wildcard (e.g., "*" or "")`) // 3. 注册 Gateway 服务 // GatewayApp 自身实现了 starter.Service 和 starter.Reloader 接口