【基于 Swoole+Hyperf 的微服务实战】第九周·周四:实现订单服务并通过网关聚合下单接口
【基于 SwooleHyperf 的微服务实战】第九周·周四实现订单服务并通过网关聚合下单接口今天我们进入第九周周四核心目标是实现订单服务并通过网关聚合下单接口完成从客户端到订单、库存、商品服务的全链路贯通。昨天我们已经有了商品和库存服务今天将创建订单服务并让网关作为统一入口整合 JWT 鉴权和限流为明天的 Saga 分布式事务奠定基础。今日目标创建order-service项目配置数据库、RPC 客户端、Consul 注册。实现订单服务的CreateOrderRPC 接口内部通过 RPC 调用库存服务冻结库存调用商品服务获取商品信息调用用户服务校验用户。在网关中创建聚合下单接口POST /orders通过 RPC 客户端调用订单服务并在此应用JWT 鉴权和限流50 QPS。测试全链路注册用户 → 登录获取 Token → 通过网关下单 → 订单生成、库存冻结、商品信息正确。验证限流效果使用压测工具确认超过 50 QPS 后返回 429。一、创建订单服务并配置环境约 45 分钟1. 创建项目docker-composeexecswoolebashcd/var/www/servicescomposercreate-project hyperf/hyperf-skeleton ordercdordercomposerrequire hyperf/database hyperf/redis hyperf/json-rpc hyperf/service-governance-consul hyperf/guzzle设置端口9505避免与现有服务冲突数据库为order_db。2. 配置数据库和 RPC 客户端.env设置数据库连接指向mysql容器库order_db。config/autoload/databases.php按环境变量配置。config/autoload/server.php添加 JSON-RPC 服务端监听9505servers[[namejsonrpc,typeServer::SERVER_HTTP,host0.0.0.0,port9505,callbacks[Event::ON_REQUEST[\Hyperf\JsonRpc\HttpServer::class,onRequest],],],],config/autoload/services.php配置 Consul 注册和消费者。消费者需添加三个依赖服务的发现consumers[[nameUserService,registry[protocolconsul,addresshttp://consul:8500],],[nameProductService,registry[protocolconsul,addresshttp://consul:8500],],[nameInventoryService,registry[protocolconsul,addresshttp://consul:8500],],],3. 数据库表结构在order_db中创建orders表通过 SQL 文件或迁移CREATETABLEorders(idBIGINTUNSIGNEDAUTO_INCREMENTPRIMARYKEY,order_idVARCHAR(64)NOTNULLUNIQUE,user_idINTNOTNULL,product_idINTNOTNULL,amountDECIMAL(10,2)NOTNULL,statusENUM(pending,frozen,paid,cancelled)NOTNULLDEFAULTpending,created_atTIMESTAMPDEFAULTCURRENT_TIMESTAMP,updated_atTIMESTAMPDEFAULTCURRENT_TIMESTAMPONUPDATECURRENT_TIMESTAMP);二、知识核心订单服务职责与同步调用流程约 1 小时订单服务的CreateOrder需要做几件事校验用户存在调用用户服务getUserById。校验商品存在且价格正确调用商品服务getProductById。调用库存服务freeze冻结库存。创建订单记录状态设为frozen。为什么不用 Saga今天先用同步调用实现基本流程保证核心链路可用。明天我们会引入 Saga 协调器将这些同步调用拆分为异步命令并增加补偿逻辑从而解决分布式事务问题。今天重点体验服务间协作和网关聚合。订单服务的接口定义我们将订单服务作为一个 RPC 服务暴露CreateOrder方法返回订单信息。也可以暴露GetOrder等。三、实战编写订单服务与网关聚合约 2.5 小时步骤 1定义订单服务契约在订单服务中创建app/JsonRpc/OrderServiceInterface.php?phpnamespaceApp\JsonRpc;useHyperf\RpcServer\Annotation\RpcService;#[RpcService(name:OrderService,protocol:jsonrpc-http,server:jsonrpc)]interfaceOrderServiceInterface{publicfunctioncreateOrder(int$userId,int$productId,float$amount):array;publicfunctiongetOrder(string$orderId):array;}步骤 2实现订单服务创建app/JsonRpc/OrderService.php?phpnamespaceApp\JsonRpc;useApp\Model\Order;useHyperf\Di\Annotation\Inject;useHyperf\RpcClient\Annotation\RpcClient;useApp\JsonRpc\UserServiceInterface;useApp\JsonRpc\ProductServiceInterface;useApp\JsonRpc\InventoryServiceInterface;useHyperf\Utils\Str;#[RpcService(name:OrderService,protocol:jsonrpc-http,server:jsonrpc)]classOrderServiceimplementsOrderServiceInterface{#[RpcClient(name:UserService)]privateUserServiceInterface$userService;#[RpcClient(name:ProductService)]privateProductServiceInterface$productService;#[RpcClient(name:InventoryService)]privateInventoryServiceInterface$inventoryService;publicfunctioncreateOrder(int$userId,int$productId,float$amount):array{// 1. 校验用户$user$this-userService-getUserById($userId);if(empty($user)){thrownew\InvalidArgumentException(用户不存在);}// 2. 校验商品$product$this-productService-getProductById($productId);if(empty($product)){thrownew\InvalidArgumentException(商品不存在);}// 金额可简单校验if($amount0){thrownew\InvalidArgumentException(金额错误);}// 3. 冻结库存数量暂定为1实际应参数化$frozen$this-inventoryService-freeze($productId,1);if(!$frozen){thrownew\RuntimeException(库存不足);}// 4. 创建订单$orderId(string)Str::uuid();Order::create([order_id$orderId,user_id$userId,product_id$productId,amount$amount,statusfrozen,]);return[order_id$orderId,user_id$userId,product_id$productId,amount$amount,statusfrozen,];}publicfunctiongetOrder(string$orderId):array{$orderOrder::where(order_id,$orderId)-first();return$order?$order-toArray():[];}}步骤 3创建模型和依赖绑定app/Model/Order.php继承Hyperf\DbConnection\Model\Model$table orders。config/autoload/dependencies.php绑定接口到实现。步骤 4启动订单服务并验证 RPC启动订单服务直接通过curl测试 RPC 接口curl-XPOST http://localhost:9505-HContent-Type: application/json\-d{jsonrpc:2.0,method:order/createOrder,params:[1,1,99.00],id:1}前提是用户 ID1 存在商品 ID1 存在且库存够应返回订单信息并检查库存服务的冻结量是否增加。步骤 5在网关中创建聚合下单接口网关需要暴露一个 RESTful 下单接口POST /orders内部调用订单服务的 RPC同时网关负责鉴权和限流。5.1 网关中增加订单服务消费者网关的config/autoload/services.php添加消费者[nameOrderService,registry[protocolconsul,addresshttp://consul:8500],],5.2 创建网关侧的订单聚合服务新建app/Service/OrderService.php或叫 OrderClientWrapper?phpnamespaceApp\Service;useHyperf\RpcClient\Annotation\RpcClient;useApp\JsonRpc\OrderServiceInterface;classOrderServiceWrapper{#[RpcClient(name:OrderService)]privateOrderServiceInterface$orderService;publicfunctioncreateOrder(int$userId,int$productId,float$amount):array{return$this-orderService-createOrder($userId,$productId,$amount);}}5.3 创建网关控制器新建app/Controller/OrderController.php?phpnamespaceApp\Controller;useApp\Service\OrderServiceWrapper;useHyperf\HttpServer\Annotation\Controller;useHyperf\HttpServer\Annotation\RequestMapping;useHyperf\Di\Annotation\Inject;#[Controller(prefix:/orders)]classOrderControllerextendsAbstractController{#[Inject]privateOrderServiceWrapper$orderService;#[RequestMapping(path:,methods:post)]publicfunctioncreate(){$userId$this-request-getAttribute(user_id);// 由 JWT 中间件注入if(!$userId){return$this-response-json([code401,message未登录])-withStatus(401);}$productId(int)$this-request-input(product_id);$amount(float)$this-request-input(amount);try{$order$this-orderService-createOrder($userId,$productId,$amount);return[code200,data$order];}catch(\Throwable$e){return$this-response-json([code400,message$e-getMessage()])-withStatus(400);}}}5.4 将/orders加入网关自身路由白名单避免被转发中间件代理。步骤 6配置限流中间件下单接口 50 QPS在网关的RateLimitMiddleware中为/orders路径设置独立限流。修改resolveLimiter方法privatefunctionresolveLimiter(string$uri):?string{if(str_starts_with($uri,/orders)){returnapi_order;}// ... 其他}并在rate_limit.php中添加api_order配置api_order[capacity50,rate50,],这样下单接口每秒最多允许 50 个请求令牌桶容量和速率都设 50可实现严格限流。步骤 7JWT 鉴权已在全局中间件中实现确保HybridAuthMiddleware或之前的JwtAuthMiddleware处于活跃状态且/orders不在白名单中因此只有携带有效 JWT 的请求才能到达控制器。四、全链路测试与压测验证约 1 小时1. 准备测试数据通过用户服务注册用户若之前未注册可 RPC 调用注册确保商品 ID1 存在库存stocks表有足够库存2. 流程测试# 登录获取 TokenTOKEN$(curl-s-XPOST http://localhost:9500/auth/login-HContent-Type: application/json-d{username:alice,password:123456}|jq-r.data.token)# 下单curl-XPOST http://localhost:9500/orders\-HAuthorization: Bearer$TOKEN\-HContent-Type: application/json\-d{product_id:1,amount:99.00}应返回订单信息状态frozen。3. 验证库存冻结curlhttp://localhost:9504-XPOST-HContent-Type: application/json\-d{jsonrpc:2.0,method:inventory/getStock,params:[1],id:1}frozen应为 1available减少。4. 限流测试使用ab或wrk以超过 50 QPS 的并发请求下单接口wrk-t4-c100-d10s--latency-HAuthorization: Bearer$TOKEN-spost_order.lua http://localhost:9500/orders需要写一个简单的 Lua 构造 POST body。观察部分请求返回 429并且后端订单服务压力可控。5. 未登录拦截测试不带 Token 请求下单接口应返回 401。五、今日作业与学习产出提交代码将order-service项目、网关聚合代码、限流配置提交到 Git。增强订单服务添加GetOrder接口供查询。记录订单操作日志事件监听写入日志或数据库。学习笔记绘制下单全链路时序图客户端 - 网关JWT限流- 订单服务 RPC - 用户/商品/库存服务 RPC - 数据库。思考同步调用库存冻结的潜在问题超时、失败为明天的 Saga 改造做铺垫。挑战任务为订单服务添加熔断器当库存服务连续失败时自动降级返回“系统繁忙”。使用 Redis 缓存商品信息减少订单服务对商品服务的 RPC 调用。今天你已经将电商系统的核心交易链路打通客户端可以安全、受控地下单库存得到实时冻结。明天我们将引入 Saga 分布式事务和延迟队列实现支付超时自动取消和补偿恢复让整个系统具备最终一致性能力。

相关新闻

Hyperf Translation 国际化组件实战指南:语言文件、占位符与复数规则的完整实现

Hyperf Translation 国际化组件实战指南:语言文件、占位符与复数规则的完整实现

后端微服务 【免费下载链接】hyperf 🚀 A coroutine framework that focuses on hyperspeed and flexibility. Building microservice or middleware with ease. 项目地址: https://gitcode.com/gh_mirrors/hy/hyperf 点击查看 免费下载 Hyperf 的翻译&…

2026/10/12 8:32:55 阅读更多 →
ORA-01623 无法删除redo

ORA-01623 无法删除redo

这个是线程2的redo,当前环境是单机,关闭线程2即可删除关闭集群模式(重启生效) ALTER SYSTEM SET cluster_databaseFALSE SCOPESPFILE;关闭线程2(如果可以直接关闭) ALTER DATABASE DISABLE THREAD 2;

2026/10/12 13:22:49 阅读更多 →
DeepSeek V4 MegaMoE与DeepGEMM:大模型推理底层优化拆解

DeepSeek V4 MegaMoE与DeepGEMM:大模型推理底层优化拆解

做算法的人学 Infra,最怕的就是被一堆源码和术语劝退。但如果你真的想把一个大模型跑明白,绕不开 GPU 底层发生了什么。今天这篇是“算法同学学 Infra 系列”的第三篇,专门讲 DeepSeek V4 里最核心的 MegaMoE 架构,以及把它跑起来…

2026/10/13 7:38:27 阅读更多 →

最新新闻

多智能体电梯群控:基于目的楼层预约与PPO的深度强化学习实现

多智能体电梯群控:基于目的楼层预约与PPO的深度强化学习实现

简介:本资源是一套面向计算机、人工智能、自动化等专业学生的毕业设计与课程设计实践项目,聚焦于多智能体协同下的电梯群控优化问题,采用深度强化学习实现目的楼层预约调度算法,解决传统电梯响应延迟高、能耗大、乘客等待时间长等…

2026/10/13 19:14:30 阅读更多 →
语义分割环境搭建:PyTorch CPU与GPU版本安装及验证全攻略

语义分割环境搭建:PyTorch CPU与GPU版本安装及验证全攻略

1. 系列前情回顾与本期核心目标语义分割学习系列写到第四篇,前面几篇我分别梳理了语义分割的基本概念、常见数据集的组织方式、以及几个主流分割模型的结构差异。按照正常的学习路线,理论铺垫到这一步,接下来就该动手了。动手的第一步就是装环…

2026/10/13 19:14:30 阅读更多 →
Spring Boot配置绑定实战:@ConfigurationProperties原理与避坑

Spring Boot配置绑定实战:@ConfigurationProperties原理与避坑

接触Spring Boot这些年,我做得最多的一个动作就是替同事定位“配置不生效”的诡异问题。排查到最后,十有八九都落在同一个注解上——ConfigurationProperties。它看起来只是给配置类加个前缀声明,实际牵扯到数据绑定、松散命名、第三方注册、…

2026/10/13 19:14:30 阅读更多 →
语义分割必备:PyTorch CPU/GPU版本安装全攻略

语义分割必备:PyTorch CPU/GPU版本安装全攻略

废话不多说,直接进入正题。这一篇是这个系列的第四篇,主题是很多人在语义分割学习中绕不过去的第一道坎——CPU和GPU版本的PyTorch及其环境安装。前几篇我们聊了语义分割的数据标注、模型结构、评价指标,但真正动手跑代码的时候,你…

2026/10/13 19:14:29 阅读更多 →
Java核心知识点全解析:从JVM到并发编程的底层原理

Java核心知识点全解析:从JVM到并发编程的底层原理

做Java开发这些年,我有一个很深的感受:真正拉开水平差距的,不是谁背的框架更多、API记得更熟,而是对底层那批核心知识点有没有吃透。所谓“Java重要知识点”,不是让你背八股文,而是帮你在遇到线上问题时&am…

2026/10/13 19:14:29 阅读更多 →
Go监听MySQL binlog实战:轻量级数据变更捕获与生产避坑指南

Go监听MySQL binlog实战:轻量级数据变更捕获与生产避坑指南

1. 这不是“监听MySQL”,而是重建一套轻量级数据变更管道很多人第一次看到“用Go监听binlog”这个说法,第一反应是:“MySQL自己不就有mysqlbinlog命令吗?再不济写个Python脚本调用一下不就完了?”——我试过&#xff0…

2026/10/13 19:13:29 阅读更多 →

日新闻

物联网系统集成避坑指南:从选型到验收的关键要点

物联网系统集成避坑指南:从选型到验收的关键要点

做了这么多年物联网系统集成的项目,我最大的感受是,大部分坑不是出在技术前沿,而是出在一些看起来很平常的地方。设备选了便宜的、协议没问清楚、现场没摸透、验收标准写得含糊——这些细枝末节,最后都会变成延期、超支和扯皮的大…

2026/10/13 0:00:57 阅读更多 →
ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

如果你玩ESP32有一阵子了,很难不碰到舵机控制这道坎。做智能小车要转方向,做机械臂要抬关节,做云台要偏航俯仰,背后都是同一个东西在干活:舵机。而我见过太多人卡在“代码能编译、舵机就是不听话”这个阶段&#xff0c…

2026/10/13 0:00:57 阅读更多 →
CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

1. 为什么CAN总线至今仍是工业现场的“硬通货”?——从汽车底盘到智能温室的真实需求你有没有拆过一辆老款燃油车的中控台?或者调试过某高校实验室里那台跑着Modbus协议却频频丢帧的PLC控制柜?又或者,在某个农业物联网项目里&…

2026/10/13 0:00:57 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/13 2:54:56 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/13 16:22:04 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/13 1:58:08 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 21:05:09 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 14:57:39 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 18:11:17 阅读更多 →