引言:为什么您的 Go 项目需要一位指挥
\n\n想象一个交响乐团。您有小提琴(用户服务)、管乐器(订单服务)、打击乐器(支付服务)。每件乐器本身都很出色,但没有指挥,它们就会各吹各的调。在 Go 微服务世界中,API 网关就是那位指挥。
\n\n当您使用 Go 编程时,您已经习惯了高性能、简洁和优雅。但一旦服务数量超过 3-4 个,混乱就开始了:客户端不知道该访问哪个地址,身份验证在每个服务中重复,监控变成了令人头疼的问题。面向 Go 服务的 API 网关可以一次性解决这些问题。
\n\n在本文中,我们不仅会讨论理论。我们将使用 Go、标准库以及几个流行的包,来构建我们自己的网关。准备好指挥了吗?我们开始吧!
\n\n什么是 API 网关,为什么在 Go 中需要它?
\n\nAPI 网关是所有客户端请求到您后端服务的单一入口点。假设您有以下服务:
\n\n- \n
user-service:8081— 注册和用户资料 \norder-service:8082— 购物车和订单 \npayment-service:8083— 交易 \n
没有网关,客户端必须知道所有三个服务的地址。有了Go 语言 API 网关,客户端只需访问 api.example.com,网关自行决定将请求转发到何处。
网关的主要功能
\n\n- \n
- 路由 — 将请求转发到相应的服务 \n
- 身份验证和授权 — 在单一位置检查 JWT 令牌 \n
- 速率限制 — 防御 DDoS 攻击和过载 \n
- 数据聚合 — 将来自多个服务的响应合并为一个 \n
- 负载均衡 — 在实例之间分配请求 \n
Go 非常适合这个角色:它轻量、快速,并且能够在单核上每秒处理数千个请求。
\n\n编写一个简单的 Go 语言 API 网关:从想法到代码
\n\n让我们构建一个最小但可用的Go API 网关。我们将只使用标准库 net/http 和用于代理的 httputil.ReverseProxy 包。
第 1 步:基本路由
\n\n从简单的开始:根据路径将请求定向到不同的服务。
\n\npackage main\n\nimport (\n "log"\n "net/http"\n "net/http/httputil"\n "net/url"\n)\n\nfunc main() {\n // 用户服务\n userServiceURL, _ := url.Parse("http://localhost:8081")\n userProxy := httputil.NewSingleHostReverseProxy(userServiceURL)\n\n // 订单服务\n orderServiceURL, _ := url.Parse("http://localhost:8082")\n orderProxy := httputil.NewSingleHostReverseProxy(orderServiceURL)\n\n http.HandleFunc("/api/users/", func(w http.ResponseWriter, r *http.Request) {\n log.Printf("将请求转发到 user-service: %s", r.URL.Path)\n userProxy.ServeHTTP(w, r)\n })\n\n http.HandleFunc("/api/orders/", func(w http.ResponseWriter, r *http.Request) {\n log.Printf("将请求转发到 order-service: %s", r.URL.Path)\n orderProxy.ServeHTTP(w, r)\n })\n\n log.Println("API 网关已启动,监听 :8080")\n log.Fatal(http.ListenAndServe(":8080", nil))\n}\n\n\n这段代码已经完成了 80% 的工作:任何对 /api/users/ 的请求都会转到 user-service,而对 /api/orders/ 的请求则会转到 order-service。但是身份验证在哪里呢?
第 2 步:添加 JWT 中间件
\n\nGo 语言 API 网关的真正魔力发生在中间件中。让我们在代理之前添加 JWT 令牌验证。
\n\npackage main\n\nimport (\n "context"\n "log"\n "net/http"\n "net/http/httputil"\n "net/url"\n)\n\nfunc main() {\n // 用户服务\n userServiceURL, _ := url.Parse("http://localhost:8081")\n userProxy := httputil.NewSingleHostReverseProxy(userServiceURL)\n\n // 订单服务\n orderServiceURL, _ := url.Parse("http://localhost:8082")\n orderProxy := httputil.NewSingleHostReverseProxy(orderServiceURL)\n\n http.HandleFunc("/api/users/", func(w http.ResponseWriter, r *http.Request) {\n log.Printf("将请求转发到 user-service: %s", r.URL.Path)\n userProxy.ServeHTTP(w, r)\n })\n\n http.HandleFunc("/api/orders/", func(w http.ResponseWriter, r *http.Request) {\n log.Printf("将请求转发到 order-service: %s", r.URL.Path)\n orderProxy.ServeHTTP(w, r)\n })\n\n log.Println("API 网关已启动,监听 :8080")\n log.Fatal(http.ListenAndServe(":8080", nil))\n}\n