【基于 Swoole+Hyperf 的微服务实战】 第五周·周一:API 网关
2026/9/11 18:21:40 网站建设 项目流程

今天我们正式引入API 网关。在微服务架构中,网关是整个系统的统一入口,负责请求路由、鉴权、限流、日志、聚合等横切关注点。今天我们将使用 Hyperf 从零搭建一个网关服务,实现对后端用户服务、文章服务和订单服务的动态路由转发,让客户端只需与网关交互,而不再直接访问各个微服务。


今日目标

  1. 理解 API 网关在微服务架构中的核心价值与常见模式。
  2. 创建独立的网关项目hyperf-gateway,配置 HTTP 服务器。
  3. 实现基础的请求转发:网关接收请求,根据路径前缀将请求透明地转发到对应的后端服务。
  4. 实现动态路由:将路由规则存储在配置中心(Nacos),支持运行时更改,无需重启网关。
  5. 测试网关转发功能,验证客户端通过网关能正常访问文章、订单等接口,并保持后端服务无感。

一、环境准备:创建网关项目(约 30 分钟)

1. 创建新项目

我们将网关作为独立的微服务项目,与hyperf-app分离。进入容器并创建新项目:

docker-composeexecswoolebashcd/var/wwwcomposercreate-project hyperf/hyperf-skeleton hyperf-gateway

等待安装完成,进入项目:

cdhyperf-gateway
2. 安装所需组件

网关需要 HTTP 服务器、JSON-RPC 客户端(用于转发?不,网关直接做 HTTP 代理即可,但也可以集成 RPC。今天先实现 HTTP 反向代理)、配置中心客户端。

composerrequire hyperf/http-server hyperf/guzzle hyperf/config-nacos

hyperf/guzzle是基于 Swoole 的协程 HTTP 客户端,用于网关转发请求时发起后端调用。

3. 配置基本环境

编辑.env,设置端口为9500(避免与 9501 冲突):

APP_NAME=Gateway APP_ENV=dev APP_PORT=9500

确认config/autoload/server.php中 HTTP 服务监听0.0.0.0:9500

4. 配置 Nacos 连接

创建config/autoload/config_center.php,写入与之前相同的 Nacos 连接信息(参考hyperf-app中的配置):

<?phpreturn['driver'=>Hyperf\ConfigNacos\NacosDriver::class,'client'=>['host'=>'nacos','port'=>8848,'username'=>'nacos','password'=>'nacos',],'config'=>['data_id'=>'gateway-routes','group'=>'DEFAULT_GROUP','namespace_id'=>env('NACOS_NAMESPACE_ID','dev-001'),'type'=>'json',],'listener'=>['enable'=>true,'interval'=>3,],];

在 Nacos 控制台的dev命名空间下新建配置gateway-routes(JSON 格式),内容暂时为空对象{},后续会填充路由规则。


二、知识核心:API 网关模式(约 1 小时)

1. 为什么需要 API 网关?

在微服务架构中,客户端直接访问各个微服务会带来诸多问题:

  • 多入口:前端需要记住多个域名/IP,增加复杂度。
  • 横切逻辑重复:每个服务都要处理鉴权、限流、日志等,代码冗余。
  • 请求聚合不便:移动端需要从多个服务拉取数据,产生多次网络往返。
  • 安全性:内部服务直接暴露,攻击面增大。

API 网关作为反向代理,统一接收所有客户端请求,然后转发到相应的后端服务,并在此过程中插入公共逻辑。

2. 网关的常见功能
  • 路由转发:根据 URL、Header 等将请求映射到具体的后端服务。
  • 认证授权:在网关层统一校验 JWT,解析用户信息并传递给下游。
  • 限流熔断:保护后端服务不被突发流量冲垮。
  • 日志与监控:记录所有请求的元数据,实现链路追踪的起点。
  • 协议转换:对外暴露 HTTP/WebSocket,对内可能调用 gRPC 或 JSON-RPC。
  • 聚合与裁剪:将多个后端响应合并为一个,减少客户端请求次数。
3. 我们今天的实现方案
  • 网关:独立的 Hyperf 项目,监听9500,内部使用Guzzle协程客户端向后端发起 HTTP 请求。
  • 路由配置:存储在 Nacos 中,格式例如:
{"routes":[{"prefix":"/api/user","target":"http://127.0.0.1:9502"},{"prefix":"/api/order","target":"http://127.0.0.1:9501"}]}
  • 动态感知:网关启动时加载路由规则,并通过 Nacos 监听变更,实时更新本地路由表。
  • 请求转发:网关接收到请求后,匹配最长路径前缀,将请求的 URI、方法、Body、Header 透传到目标服务,并将响应原样返回客户端。

三、实战:构建动态路由转发网关(约 2.5 小时)

步骤 1:创建路由管理服务

新建app/Service/RouteService.php,负责加载、缓存和匹配路由:

<?phpnamespaceApp\Service;useHyperf\Contract\ConfigInterface;useHyperf\Di\Annotation\Inject;classRouteService{#[Inject]privateConfigInterface$config;privatearray$routes=[];publicfunctionloadRoutes():void{// 从 Nacos 配置中心读取 routes 节点$this->routes=$this->config->get('routes',[]);}/** * 根据请求URI匹配目标服务地址 */publicfunctionmatch(string$uri):?string{// 按前缀长度降序排序,优先匹配更具体的路径$matched=null;$maxLength=0;foreach($this->routesas$route){$prefix=$route['prefix'];if(str_starts_with($uri,$prefix)&&strlen($prefix)>$maxLength){$maxLength=strlen($prefix);$matched=$route['target'];}}return$matched;}}
步骤 2:网关核心中间件:转发请求

创建app/Middleware/GatewayMiddleware.php,实现核心转发逻辑:

<?phpnamespaceApp\Middleware;useApp\Service\RouteService;useHyperf\Di\Annotation\Inject;useHyperf\Guzzle\ClientFactory;usePsr\Http\Message\ResponseInterface;usePsr\Http\Message\ServerRequestInterface;usePsr\Http\Server\MiddlewareInterface;usePsr\Http\Server\RequestHandlerInterface;classGatewayMiddlewareimplementsMiddlewareInterface{#[Inject]privateRouteService$routeService;#[Inject]privateClientFactory$clientFactory;publicfunctionprocess(ServerRequestInterface$request,RequestHandlerInterface$handler):ResponseInterface{$uri=$request->getUri()->getPath();$target=$this->routeService->match($uri);if(!$target){// 未匹配路由,返回 404return\Hyperf\Utils\ApplicationContext::getContainer()->get(\Hyperf\HttpServer\Contract\ResponseInterface::class)->json(['code'=>404,'message'=>'Gateway: route not found'])->withStatus(404);}// 构造目标 URL(保留查询参数)$query=$request->getUri()->getQuery();$targetUrl=$target.$uri.($query?'?'.$query:'');try{// 使用协程 Guzzle 客户端转发请求$client=$this->clientFactory->create();$response=$client->request($request->getMethod(),$targetUrl,['headers'=>$request->getHeaders(),'body'=>(string)$request->getBody(),'timeout'=>5,// 超时时间]);// 将后端响应转换为 PSR-7 响应并返回return\Hyperf\Utils\ApplicationContext::getContainer()->get(\Hyperf\HttpServer\Contract\ResponseInterface::class)->withStatus($response->getStatusCode())->withBody(new\Hyperf\HttpMessage\Stream\SwooleStream($response->getBody()->getContents()))->withHeaders($response->getHeaders());}catch(\Throwable$e){// 转发失败,返回 502 Bad Gatewayreturn\Hyperf\Utils\ApplicationContext::getContainer()->get(\Hyperf\HttpServer\Contract\ResponseInterface::class)->json(['code'=>502,'message'=>'Gateway error: '.$e->getMessage()])->withStatus(502);}}}
步骤 3:注册中间件并配置路由

config/autoload/middlewares.php中添加全局中间件:

<?phpreturn['http'=>[\App\Middleware\GatewayMiddleware::class,],];

同时确保config/routes.php中没有任何特定路由,因为所有请求都应该进入网关中间件进行转发。可以删除或保留默认的闭包路由,网关中间件会拦截并处理。

步骤 4:配置 Nacos 路由规则

打开 Nacos 控制台(http://localhost:8848/nacos),在dev命名空间下编辑gateway-routes,写入:

{"routes":[{"prefix":"/api/user","target":"http://hyperf-app:9502"},{"prefix":"/api/product","target":"http://hyperf-app:9504"},{"prefix":"/articles","target":"http://hyperf-app:9501"},{"prefix":"/orders","target":"http://hyperf-app:9501"},{"prefix":"/auth","target":"http://hyperf-app:9501"}]}

注意:因为网关和hyperf-app在同一个 Docker 网络中,可以使用容器名hyperf-app(需确认 Docker Compose 中服务名,或者直接用127.0.0.1,但推荐容器名)。如果服务都在宿主机网络,可以用host.docker.internal127.0.0.1

步骤 5:启动网关并测试转发

启动网关服务(端口 9500):

php bin/hyperf.php start

测试通过网关访问文章列表:

curlhttp://localhost:9500/articles

应返回文章列表数据(来自hyperf-app:9501)。
测试订单详情:

curlhttp://localhost:9500/orders/1

应返回聚合订单数据,包括用户和商品信息(内部 RPC 调用仍然在hyperf-app内完成)。
测试用户服务直接通过网关:

curl-XPOST http://localhost:9500/api/user-H"Content-Type: application/json"-d'{"jsonrpc":"2.0","method":"user/GetUserById","params":[1],"id":1}'

注意:用户服务原本是 JSON-RPC 接口,但网关目前透明转发 HTTP,所以只要路径前缀匹配,网关就会转发请求到9502的 JSON-RPC 处理器,因此能正常工作。

验证动态路由:在 Nacos 中修改gateway-routes,添加一个前缀/test指向http://hyperf-app:9501,发布。稍等几秒,访问curl http://localhost:9500/test,预期能到达hyperf-app的默认路由(可能 404,但说明网关已加载新路由)。无需重启网关。


四、成果测试与验证(约 1 小时)

1. 测试清单
检验项方法通过标准
网关启动并监听curl http://localhost:9500无路由时返回 404网关响应,状态码 404 由网关生成
路由匹配成功curl http://localhost:9500/articles返回文章列表,与直接访问 9501 一致
路径前缀匹配新增路由/orders指向 9501能正确获取订单
不存在的路由curl http://localhost:9500/nonexist返回 404,由网关提供
动态添加路由在 Nacos 新增路由,等待后访问新路由生效,无需重启
后端服务故障暂时停止 9501 服务,访问/articles返回 502,网关提示错误
请求方法透传curl -X POST http://localhost:9500/auth/login ...正常登录,说明 POST 和 Body 正确转发
查询参数透传curl http://localhost:9500/articles?page=1分页参数生效
2. 常见问题与调试
  • 无法连接后端:检查网关能否解析hyperf-app容器名,可在网关容器内ping hyperf-app或直接用 IP。
  • 路由不生效:检查 Nacos 连接配置,确认gateway-routes的 Data ID 正确;观察网关日志是否有配置更新。
  • Guzzle 超时:后端处理慢可能导致超时,调整timeout参数,并考虑异步化或增加重试。
3. 性能初探

网关增加了一层网络转发,延迟会略微上升。通过ab对比直连和网关访问的响应时间,可以看到网关引入的延迟在毫秒级,但带来的架构收益巨大。


五、今日作业与学习产出

  1. 提交代码:将hyperf-gateway项目提交到 Git,包括GatewayMiddlewareRouteService、配置等。
  2. 增强网关
    • 为网关添加请求日志中间件,记录每个请求的方法、路径、状态码和耗时。
    • 实现请求重试:转发失败时,尝试重试一次(适用于幂等 GET 请求)。
  3. 学习笔记
    • 画出网关在微服务架构中的位置图,标出数据流。
    • 对比 API 网关与反向代理(Nginx)的异同,思考为什么需要应用层网关。
  4. 挑战任务
    • 研究 Hyperf 的协程 HTTP 客户端,了解hyperf/guzzle的连接池配置,优化网关并发性能。
    • 实现基于请求头的路由(如X-Group: v2),实现更灵活的灰度分流。

通过今天的学习,你已经成功构建了一个灵活、可动态配置的 API 网关,为整个微服务系统提供了统一入口。明天我们将为网关加上全局 JWT 鉴权,并实现向下游服务透传用户信息。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询