# mall-microservice-lab **Repository Path**: middleware-lab/mall-microservice-lab ## Basic Information - **Project Name**: mall-microservice-lab - **Description**: 基于 go-zero 的电商微服务中间件实战项目。项目目标不是做完整商城,而是通过电商下单链路系统练习 PostgreSQL、Redis、RabbitMQ、Elasticsearch、MinIO、Etcd、Prometheus、Grafana、Jaeger 等常见后端中间件。 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-06-30 - **Last Updated**: 2026-07-09 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # go-zero-mall-middleware-lab 基于 go-zero 风格、面向后端中间件实战的微服务实验项目。MVP 版本已完成可运行的下单主链路。 > **MVP 实现说明**:为了快速跑通主流程,本 MVP 用 Go 标准库 `net/http` 实现 HTTP-RPC(不依赖 goctl/protoc),网关用配置驱动的反向代理调用 RPC。`*.api`、`*.proto` 文件保留作为未来用 goctl 重新生成 go-zero 代码的契约。 ## 主链路 ```text gateway-api ──HTTP──▶ user-rpc / product-rpc / inventory-rpc / order-rpc / payment-rpc │ │ ▼ ▼ PostgreSQL ◀────── Redis ──────▶ RabbitMQ │ ▼ order-mq, notify-mq ``` ## MVP 已实现的能力 - **注册 / 登录 / JWT 鉴权**(`user-rpc` + `gateway-api`,bcrypt + HS256) - **商品详情缓存**:`product-rpc` 用 Redis 实现 cache-aside,并写入空值键防穿透 - **商品写接口 + 变更事件**:`product-rpc` 支持创建(含 SKU + 库存初始化)/ 更新 / 下架,写后失效缓存并发布 `product.created/updated/deleted` 事件到 `mall.product.exchange` - **搜索增量同步**:`search-mq` 消费商品变更事件,按 `product_id` 回读 DB 后 `IndexProduct`/`DeleteProduct`,实现 DB->ES 最终一致性(启动时全量重建) - **文件级联清理**:`file-mq` 消费 `product.deleted`,自动删除该商品绑定的 MinIO 对象 + `file_objects` 记录 - **库存锁定模式**:`inventory-rpc` 用条件 SQL 完成 `lock / release / confirm`,乐观防超卖 - **下单事务 + 幂等**:`order-rpc` 用 `request_id` Redis SetNX 拦截重复提交,单事务写订单 + 明细 + 状态日志 - **优惠券**:`coupon-rpc` 满减券(领券 / 锁定 / 确认 / 释放),下单时 saga 式锁定并计算折扣 `pay_amount`,支付后确认、关单时释放,失败自动补偿 - **失败补偿**:订单写入失败时自动调用 `inventory/release` + `coupon/release` 回滚锁定 - **支付模拟**:`payment-rpc` 写入支付单后发布 `payment.success` 事件 - **MQ 一致性**:`order-mq` 消费 `payment.success` 调 `order-rpc` 改状态、确认库存,`mq_consume_logs` 提供消费幂等 - **超时关单**:`order-mq` 周期扫描 `orders.expire_at`,关单 + 释放库存 - **购物车**:`cart-rpc` 用 Redis Hash(`mall:cart:user:`)存储,加购 `HINCRBY` 累加合并、设置数量 / 列出 / 删项 / 清空,写操作刷新 TTL(默认 7 天) - **通知日志**:`notify-mq` 订阅 `order.created` / `payment.success`,落 `mq_consume_logs` 并打印通知 - **统一响应 + 错误码**:`common/response`、`common/xerr` ## 端口与服务 | 服务 | 端口 | 入口 | | --- | --- | --- | | `gateway-api` | 8000 | `app/gateway/api` | | `user-rpc` | 9001 | `app/user/rpc` | | `product-rpc` | 9002 | `app/product/rpc` | | `inventory-rpc` | 9003 | `app/inventory/rpc` | | `order-rpc` | 9004 | `app/order/rpc` | | `payment-rpc` | 9006 | `app/payment/rpc` | | `search-rpc` | 9007 | `app/search/rpc` | | `file-api` | 9005 | `app/file/api` | | `cart-rpc` | 9008 | `app/cart/rpc` | | `coupon-rpc` | 9009 | `app/coupon/rpc` | | `order-mq` | 9104 (metrics) | `app/order/mq` | | `notify-mq` | 9105 (metrics) | `app/notify/mq` | | `search-mq` | 9106 (metrics) | `app/search/mq` | | `file-mq` | 9107 (metrics) | `app/file/mq` | ## 快速开始 ```bash # 1. 启动中间件(PostgreSQL/Redis/RabbitMQ/Etcd 等) make compose-up # init.sql / seed.sql 会被 postgres 容器自动加载(mall 库 + demo 商品 + sku 30001 库存 100) # 2. 跑测试 + 构建 make test make build # 3. 一键启动 14 个服务(日志在 logs/) make run-all # 4. 端到端烟囱测试(注册->登录->查商品->下单->重复拒绝->支付->看状态->购物车->优惠券领/锁/确认/释放->商品创建/更新/下架+ES增量同步+文件级联清理->搜索->文件上传/下载/预签名/删除) make smoke # 5. 停止 make stop-all make compose-down ``` 需要单独前台运行某个服务时: ```bash make run-gateway make run-order # ... ``` ## 关键 API | Method | 路径 | 说明 | | --- | --- | --- | | POST | `/api/v1/user/register` | 注册 | | POST | `/api/v1/user/login` | 登录,返回 JWT | | GET | `/api/v1/products/:id` | 商品详情(Redis 缓存) | | POST | `/api/v1/products` | 创建商品(需 Authorization Bearer,含可选 SKU + 库存初始化,发布 `product.created` 事件) | | PUT | `/api/v1/products/:id` | 更新商品(发布 `product.updated`,失效缓存,触发 ES 增量同步) | | DELETE | `/api/v1/products/:id` | 下架商品(软删 status=0,发布 `product.deleted`,触发 ES 移除 + 文件级联清理) | | GET | `/api/v1/search/products` | 商品搜索(Elasticsearch,支持 `keyword/category_id/min_price/max_price/page/page_size`) | | POST | `/api/v1/orders` | 创建订单(需 Authorization Bearer,幂等 by `request_id`) | | GET | `/api/v1/orders/:order_no` | 订单详情 | | POST | `/api/v1/orders/:order_no/pay` | 模拟支付 | | POST | `/api/v1/cart/items` | 加购(需 Authorization Bearer,`sku_id`+`quantity`,同 SKU 用 HINCRBY 累加合并) | | PUT | `/api/v1/cart/items` | 设置数量(`quantity<=0` 删除该项) | | GET | `/api/v1/cart/items` | 购物车列表(按 `sku_id` 升序) | | DELETE | `/api/v1/cart/items/:sku_id` | 删除单个 SKU | | DELETE | `/api/v1/cart/items` | 清空购物车 | | POST | `/api/v1/coupons/claim` | 领取优惠券(需 Authorization Bearer,`coupon_id` 模板 id) | | GET | `/api/v1/coupons` | 我的优惠券列表(可选 `status` 过滤:1=未用 2=锁定 3=已用) | | POST | `/api/v1/files/upload` | multipart 上传(鉴权,5MB,白名单 MIME,自动选择 public/private bucket) | | POST | `/api/v1/files/presign-put` | 取客户端直传 URL(PUT) | | POST | `/api/v1/files/presign-get` | 取私有对象的临时 GET URL | | DELETE | `/api/v1/files` | 按 `bucket+object_key` 删除(仅上传者) | 下单请求体示例: ```json { "sku_id": 30001, "quantity": 1, "request_id": "client-uuid-xxx", "coupon_id": 0 } ``` > `coupon_id` 可选,传领取返回的 `user_coupon_id` 即可使用满减券(下单锁定、支付确认、关单释放)。 ## 中间件入口 - RabbitMQ Management: (guest/guest) - RabbitMQ Prometheus exporter: - MinIO Console: (mall / mall123456) - Elasticsearch: ,Kibana: - Prometheus: (含 `/alerts` 告警规则),Grafana: (匿名 Admin 已开启,看板 `Mall Overview` + `Mall RED/USE`) - Jaeger: (OTLP HTTP `:4318`、gRPC `:4317`) ## 可观测性 - 每个 HTTP 服务在自身端口暴露 `/metrics`(如 `:8000/metrics`),MQ worker 暴露在 9104/9105/9106/9107。 - Prometheus 抓取 `host.docker.internal` 上的 14 个服务 + RabbitMQ Prometheus 插件;告警规则见 `deploy/prometheus/rules/alerts.yml`(9 条,覆盖 RED/业务/MQ/DB·缓存)。 - Grafana 数据源(`uid: Prometheus`)、看板已自动 provisioning:`Mall Overview` 总览 + `Mall RED/USE` 视图。 - OpenTelemetry trace 通过 OTLP HTTP 上报 Jaeger;跨 gateway→RPC、RPC→RabbitMQ→消费者全链路传播。 - DB 全量埋点:`common/db` 用 `sqlhooks` 包裹 `lib/pq`,每条 SQL 自动生成挂在请求 trace 下的 `db.` span(Jaeger 里 gateway→RPC→DB 全程可见),并复用既有 `db_query_duration_seconds` / `db_slow_query_total` 指标覆盖所有服务(保持单一 Prometheus 指标路径)。 - 结构化日志:`common/xlog` 基于 zerolog 输出 JSON,带 `service` 字段;标准库 `log` 被重定向,现有日志零改造即结构化;`xlog.FromContext(ctx)` 自动附加 `trace_id`/`span_id`,HTTP 访问日志与关键 MQ/补偿链路均带 trace 上下文。 - 详细指标清单、PromQL、DB 追踪、结构化日志与坑点见 `docs/observability.md`。 ## 目录 - `app//(api|rpc|mq)/`:服务实现 + `etc/*.yaml` 配置 - `common/`:可复用包 - `auth` JWT,`config` YAML 加载,`db` Postgres 池,`redisx` Redis 客户端,`mq` RabbitMQ helper - `httpx` HTTP server/client + 统一响应封装 - `esx` Elasticsearch、`minio` 对象存储、`xmetrics` Prometheus 指标、`xtrace`/`xobs` OpenTelemetry - `xerr`、`xredis`、`xmq`、`xid`、`constant` 等领域常量 - `deploy/docker-compose.yml`:中间件编排(含 Prometheus / Grafana / Jaeger provisioning) - `script/`:`init.sql`、`seed.sql`、`run-all.sh`、`smoke.sh` - `docs/`:架构 / 数据库 / 中间件实践文档(含 `observability.md`) - `PLAN.md`:完整项目蓝图与后续阶段 ## 排障 - **本地 6379 端口冲突**:若宿主装了 Homebrew Redis(`brew services` 自启),它会占用 `127.0.0.1:6379` 并要求认证,导致服务报 `NOAUTH`。先 `brew services stop redis` 让 Docker Redis 接管。 - 其它常见坑见各 `docs/*-practice.md` 与 `docs/observability.md`。 ## 已完成阶段(对照 PLAN.md) 1. 阶段 1–5:用户/商品/库存/订单/支付主链路 + RabbitMQ 最终一致性 ✅ 2. 阶段 2(购物车):`cart-rpc` Redis Hash 购物车(加购累加 / 设置 / 列出 / 删项 / 清空)✅ 3. 阶段 6:Elasticsearch 商品搜索(`search-rpc` + `search-mq`,含商品写接口 + 变更事件增量同步)✅ 4. 阶段 7:MinIO 文件服务(`file-api` 上传 / 预签名 / 删除 + `file-mq` 商品下架级联清理)✅ 5. 阶段 8:Prometheus 指标 + Grafana 看板 + Jaeger 链路追踪 + DB 全量埋点 + 结构化日志(trace_id 关联)✅ 6. 优惠券:`coupon-rpc` 满减券(领券 / 锁定 / 确认 / 释放,下单链路 saga 集成 + 补偿)✅ ## 下一步 1. 替换 HTTP-RPC 为 zrpc + Etcd 服务发现(用 goctl 生成) 2. 接入 alertmanager 并配置钉钉/飞书 webhook 推送告警通知