# by-shop-cloud **Repository Path**: nieps/by-shop-cloud ## Basic Information - **Project Name**: by-shop-cloud - **Description**: 分布式电商 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2025-09-29 - **Last Updated**: 2025-11-07 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 分布式项目 ## 目标 * 后端 尽可能用到所有的分布式组件 > 1. 讲过的都要用 > 2. 扩展: spring security 、 链路追踪 、es 、canal > 3. 尝试使用设计模式 (你会哪些设计模式?) > 4. 尝试引入ELK (入门) (如何定义问题) > 5. 尝试用kafka * 前端 > 1. vue3 组合式 (建议用选项式实现一个功能 ) > 2. vue-router路由 > 3. pinna 状态管理 (多组件数据共享) > 4. 尝试使用uniapp 实现移动端 * 打包运维 > 1. docker镜像 配置ci cd > 2. 尝试引入docker-compose > 3. 接入k8s ## 如何搭建 采用:父---子---孙子 三层结构 基本模块: * api 用于微服务间通信 * common 通用模块 * 网关 * 业务 ### 配置中心 #### 全局配置 1. 进入 Nacos 控制台 → 「配置管理」→ 「配置列表」; 2. 点击「+」新建配置,填写以下信息: - **Data ID**:建议命名为 `global-public.yaml`(明确标识 “全局公共配置”); - **Group**:建议使用 `GLOBAL_GROUP`(与业务配置分组区分); - **配置格式** yaml - 内容 ~~~yaml spring: application: name: system-service #服务名 cloud: nacos: server-addr: 127.0.0.1:8848 #nacos 服务 地址 username: nacos password: nacos config: file-extension: yaml shared-configs: - data-id: global-public.yaml #全局配置 group: GLOBAL_GROUP refresh: true ~~~ ## 设计 > 相关的问题: > > 1. 如何防止超卖 > 2. 什么是spu sku > 3. 订单未支付自 动取消 ### 商品 ![image-20250930103334213](assets/image-20250930103334213.png) > 1. spu > 2. spu-detail > 3. sku > 4. sku-stock > 5. spu-extendsion > 6. att > 7. attr_value > 8. spu _attr-value ​ 规格: 1. 颜色 值: 1 2 3 4 5 6 7 spu1: 1 1 ​ 1 2 spu2: 1 3 ​ 1 5 ​ 1 7 ​ 3个规格 每个规格有2值: ## saas 多 租户: 数据隔离 、 账号id: 平台端:----- 查看所有的信息 店铺端: 查看跟自己相关的 会员: 3张三: 1%3 表1: 表2: 表0: ## 图片上传 缩略图 800\*800 -----生成一个80\*80 自定义字段 : ----直接使用json格式 定义 ## 微服务拆分 * 商品服务 (基于spu sku ) > 扩展: 自定义字段 参考json 字段 * 订单服务 > 扩展: 优惠券相关的 。。。。 * 秒杀服务 ---- > 基于sku redis预减库存 抛mq消息 * redis购物车 > 数据库表 -----在redis中的商品信息 长时间没有下单 ----同步到数据库 ---从 redis删除 节省内存资源 > > 用户查看----redis有直接返回 ---没有查数据库-(有加入reids返回) > > xxl-job ---定时 * 支付服务 > 模拟实现 支付宝 ----- > > 聚合支付接口 * 商品搜索服务 es ---- * 会员服务 ---- * 商家服务 * rbac ---服务 ## 商品服务 ### PC端 商家端: (根据商家id ) /api/v1 * 商品发布 * 商品列表 * 修改功能 * 上下架 平台端: * 商品审核 (查看所有商家待审核的商品) * 通过 * 拒绝 移端端: /m/v1 * 首页商品列表 分页功能 * 根据分类 查询商品列表 * 商品详情 * feign接口 供其它微服务调用 /fegin # spring cloud 分布式架构 最佳实践 Spring Cloud 作为基于 Spring Boot 的分布式系统开发工具集,其最佳实践围绕**稳定性、可扩展性、可观测性、安全性**和**开发效率**五大核心目标展开。以下是经过行业验证的分层最佳实践指南,涵盖架构设计、组件选型、配置、部署等关键环节。 ## 一、架构设计最佳实践 分布式架构的根基是 “合理拆分” 与 “清晰边界”,避免过度设计或耦合。 ### 1. 微服务拆分原则 微服务拆分是分布式架构的第一步,直接决定后续维护成本,需遵循以下原则: - **单一职责**:一个微服务只专注于解决一个业务领域问题(如 “用户服务” 仅负责用户注册、认证、信息管理,不包含订单逻辑)。 - **高内聚低耦合**:服务内部组件紧密关联,服务间通过定义清晰的 API 通信,避免直接依赖数据库或内部方法。 - **按业务域拆分**:优先按业务模块(如用户域、订单域、支付域)拆分,而非技术层(如 “缓存服务”“数据库服务” 这类纯技术拆分易导致跨域依赖)。 - **粒度适中**:避免 “过大服务”(单体迁移换皮)或 “过小服务”(分布式调用链过长,如拆分为 “用户姓名服务”“用户年龄服务”)。通常以 “团队能独立维护”“发布周期不影响其他服务” 为衡量标准。 ### 2. 领域驱动设计(DDD)落地 复杂业务场景下,建议用 DDD 指导拆分: - 通过**领域模型**定义业务边界,将 “聚合根”(如`Order`、`User`)作为微服务的核心实体。 - 服务间通过**领域事件**(Domain Event)异步通信(如 “订单支付成功” 事件触发 “库存扣减”“物流创建”),减少同步依赖。 ## 二、核心组件选型与最佳实践 Spring Cloud 组件众多,需根据业务场景选择稳定、轻量的方案,避免 “为了用组件而用组件”。以下是主流组件的选型建议和实践要点: | 组件类型 | 推荐组件 | 替代 / 淘汰组件 | 核心最佳实践 | | ------------ | -------------------------- | ------------------- | ------------------------------------------------------------ | | 服务注册发现 | Spring Cloud Alibaba Nacos | Eureka、Consul | 1. 开启服务健康检查(心跳 + 接口探测),及时剔除不健康实例;2. 生产环境使用集群部署(至少 3 节点),避免单点故障。 | | 配置中心 | Spring Cloud Alibaba Nacos | Spring Cloud Config | 1. 配置按 “环境(dev/test/prod)+ 服务名” 分层,避免全局配置混乱;2. 敏感配置(如数据库密码)加密存储(Nacos 支持 AES 加密)。 | | 负载均衡 | Spring Cloud LoadBalancer | Ribbon(已停更) | 1. 生产环境优先使用**轮询 + 权重**策略(按实例性能分配权重);2. 结合服务健康状态动态调整负载(不健康实例不接收流量)。 | | 服务熔断降级 | Resilience4j | Hystrix(已停更) | 1. 熔断阈值:失败率 > 50% 且请求数 > 20 时触发熔断;2. 降级策略:返回默认值(如空列表)或缓存数据,避免级联失败。 | | API 网关 | Spring Cloud Gateway | Zuul(性能差) | 1. 网关职责:路由转发、认证授权、限流、日志;2. 避免在网关处理复杂业务逻辑(如数据聚合),防止成为瓶颈。 | | 分布式事务 | Seata(AT 模式) | 2PC(性能差) | 1. 非核心业务优先用**最终一致性**(如可靠消息队列);2. 核心业务(如订单 - 支付)用 Seata AT 模式(无侵入),注意事务超时时间设置。 | | 分布式追踪 | SkyWalking/Zipkin | - | 1. 全链路追踪:采集 “服务调用链、耗时、异常” 数据;2. 关键业务链路设置采样率 100%,非关键链路采样率 10%(降低性能损耗)。 | ## 三、服务通信最佳实践 服务间通信是分布式架构的核心,需平衡 “效率” 与 “可靠性”。 ### 1. 通信协议选择 | 协议类型 | 适用场景 | 优点 | 缺点 | | -------- | -------------------------------------- | --------------------------------------------- | ---------------------------- | | REST API | 跨语言、外部系统集成 | 简单、易调试、兼容 HTTP 生态 | 序列化效率低、连接开销大 | | gRPC | 内部服务高频通信(如微服务间数据同步) | 基于 HTTP/2,二进制序列化(Protobuf),效率高 | 学习成本高、浏览器不支持 | | 消息队列 | 异步通信(如事件通知) | 解耦、削峰填谷、提高吞吐量 | 引入中间件依赖、一致性需保障 | **实践建议**:内部服务优先用 gRPC 提升性能,外部暴露接口用 REST API;异步场景(如日志同步、通知)用 Kafka/RabbitMQ。 ### 2. 接口设计规范 - **版本控制**:URL 路径带版本(如`/api/v1/orders`),避免接口变更影响老客户端。 - 统一响应格式 :所有 API 返回统一结构,包含状态码、消息、数据: json ```json { "code": 200, // 200成功,4xx客户端错,5xx服务端错 "message": "success", "data": {...} // 业务数据 } ``` - **幂等性设计**:接口需支持重复调用(如支付回调),通过唯一 ID(如订单号)去重,避免重复处理。 ## 四、配置与部署最佳实践 ### 1. 配置管理 - 分层配置 :按 “全局配置> 环境配置 > 服务配置 > 实例配置” 优先级覆盖(实例配置优先级最高)。 示例:Nacos 配置 Data ID 格式为 ``` ${service-name}-${profile}.yaml ``` (如 ``` order-service-prod.yaml ``` )。 - **动态配置**:非核心配置(如日志级别)支持动态刷新(`@RefreshScope`注解),核心配置(如数据库连接)需重启生效。 - **配置审计**:开启 Nacos 配置变更日志,记录 “谁改了什么、什么时候改的”,便于故障回溯。 ### 2. 容器化与编排 - **容器化**:所有微服务用 Docker 打包,镜像标签包含版本号(如`order-service:1.0.0`),避免`latest`标签导致版本混乱。 - 编排工具 :生产环境用 Kubernetes(K8s)管理容器,实现: - **自动扩缩容**:基于 CPU / 内存使用率动态调整实例数(如 CPU>80% 时扩容,<30% 时缩容)。 - **滚动更新**:无感知发布(先启动新实例,再停止老实例),避免服务中断。 - **健康检查**:K8s 存活探针(livenessProbe)和就绪探针(readinessProbe)确保实例健康后接收流量。 ## 五、可观测性最佳实践 “看不见的问题无法解决”,可观测性是分布式架构稳定运行的保障,需实现 “日志、监控、追踪” 三位一体。 ### 1. 日志管理 - 统一日志格式 :包含 ``` 时间戳、服务名、traceId、日志级别、内容 ``` ,便于跨服务串联日志(如通过 traceId 定位全链路日志)。 示例: ``` 2024-05-20 14:30:00 [order-service] [traceId:abc123] [INFO] 订单创建成功,订单号:123456 ``` - **日志分级**:ERROR(业务异常)、WARN(潜在风险)、INFO(关键操作)、DEBUG(开发调试,生产关闭)。 - **集中收集**:用 ELK(Elasticsearch+Logstash+Kibana)或 Loki 收集日志,支持全文检索和可视化分析。 ### 2. 监控告警 - 核心指标监控 : - 系统指标:CPU、内存、磁盘 IO、网络流量。 - 应用指标:接口 QPS、响应时间(P95/P99)、错误率、线程池状态。 - 业务指标:订单量、支付成功率、活跃用户数。 - **监控工具**:Prometheus + Grafana,配置告警规则(如错误率 > 1%、响应时间 P95>500ms 时触发告警)。 - **告警渠道**:优先用企业微信 / 钉钉 / 短信,避免邮件告警被忽略;按严重程度分级(P0 致命、P1 严重、P2 一般),P0 告警需电话通知负责人。 ### 3. 全链路追踪 - **工具选择**:Apache SkyWalking(性能好、支持多语言)或 Spring Cloud Sleuth+Zipkin(轻量)。 - **关键链路追踪**:对核心业务(如 “下单 - 支付 - 发货”)强制开启全采样,非核心链路按比例采样(如 10%),降低性能损耗。 - **链路分析**:通过追踪数据定位慢调用(如某个 SQL 耗时过长)、服务依赖瓶颈(如 A 服务频繁调用 B 服务导致 B 过载)。 ## 四、稳定性与容错最佳实践 分布式系统 “故障必然发生”,需提前做好容错设计。 ### 1. 服务熔断与降级 - **熔断触发条件**:基于 “失败率” 和 “请求量” 双重阈值(如 10 秒内失败率 > 50% 且请求数 > 20),避免单个异常请求触发熔断。 - 降级策略 : - 静态降级:返回默认值(如 “服务繁忙,请稍后重试”)。 - 动态降级:返回缓存数据(如 Redis 缓存的热门商品列表)。 - 熔断后恢复:采用 “半开” 状态(尝试转发少量请求,成功则恢复,失败则继续熔断)。 ### 2. 限流保护 - **限流粒度**:按 “接口”“用户 ID”“IP” 限流,避免单个用户 / 接口压垮服务。 - **限流算法**:生产环境用**令牌桶算法**(支持突发流量,平滑限流),如 Gateway 集成 Resilience4j 实现限流。 - **限流阈值**:基于压测结果设置(如接口 QPS 上限 = 压测峰值的 80%),避免 “一刀切”。 ### 3. 灾备与高可用 - **集群部署**:所有核心组件(Nacos、Gateway、数据库)至少 3 节点集群,避免单点故障。 - **多可用区部署**:跨机房部署(如阿里云多可用区),单个机房故障时,流量自动切换到其他机房。 - **数据备份**:数据库定时备份(如 MySQL 主从复制 + 每日全量备份),Redis 开启 AOF+RDB 持久化。 ## 五、安全性最佳实践 分布式系统暴露面广,需从 “认证、授权、数据加密” 多维度防护。 ### 1. 统一认证授权 - **OAuth2.0 + JWT**:实现统一认证(如通过 Spring Cloud Security OAuth2),客户端通过令牌(JWT)访问服务,避免重复登录。 - **令牌管理**:JWT 设置合理过期时间(如 1 小时),刷新令牌(Refresh Token)过期时间更长(如 7 天);敏感操作(如修改密码)需重新验证身份。 ### 2. 接口安全 - **HTTPS 加密**:所有外部接口(包括 Gateway、前端调用)强制使用 HTTPS,防止数据传输被窃取。 - **接口鉴权**:基于 RBAC(角色权限控制)模型,限制用户只能访问有权限的接口(如 “普通用户” 不能调用 “订单删除” 接口)。 - **防刷防护**:对高频接口(如登录、短信验证码)添加验证码、IP 黑名单、请求频率限制。 ### 3. 数据安全 - **敏感数据加密**:数据库敏感字段(如手机号、身份证号)加密存储(如 AES 加密),配置中心敏感配置加密。 - **数据脱敏**:日志、接口返回中脱敏敏感数据(如手机号显示为`138****5678`)。 ## 六、开发与协作最佳实践 ### 1. 代码规范 - **统一技术栈**:所有微服务使用相同版本的 Spring Boot/Spring Cloud(如 Spring Boot 2.7.x + Spring Cloud Alibaba 2021.0.5.0),避免版本兼容问题。 - **代码审查**:通过 GitLab CI/CD 强制代码审查(MR 需至少 1 人审批通过),禁止直接推送到主分支。 - **单元测试**:核心业务逻辑单元测试覆盖率 > 80%,避免低级 Bug 进入生产。 ### 2. CI/CD 流水线 - **自动化构建**:代码提交后自动编译、打包、跑测试用例(如用 Jenkins/GitLab CI)。 - **环境隔离**:严格区分 dev(开发)、test(测试)、pre(预发)、prod(生产)环境,避免环境配置不一致导致问题。 - **灰度发布**:生产环境发布时先部署少量实例(如 10%),观察无异常后再全量发布,降低风险。 ## 总结 Spring Cloud 分布式架构的最佳实践并非一成不变,需结合**业务规模、团队能力、技术成熟度**灵活调整。核心原则是: 1. **优先稳定**:在性能和稳定性之间,优先保障稳定性(如熔断、限流、降级)。 2. **拒绝过度设计**:不盲目追求 “高大上” 组件,能用简单方案解决的问题,不引入复杂中间件。 3. **可观测先行**:在架构初期就接入监控、日志、追踪,避免 “出问题后无法定位”。 通过以上实践,可构建出 “高可用、可扩展、易维护” 的分布式系统,支撑业务持续增长。 # nacos 配置中心 全局配置 1. 进入 Nacos 控制台 → 「配置管理」→ 「配置列表」; 2. 点击「+」新建配置,填写以下信息: - **Data ID**:建议命名为 `global-public.yaml`(明确标识 “全局公共配置”); - **Group**:建议使用 `GLOBAL_GROUP`(与业务配置分组区分); - **Namespace**:选择对应环境(如 `dev`,确保环境隔离); - **配置格式**:根据项目习惯选择(如 YAML、Properties); - 配置内容 :填写公共配置,示例(YAML 格式): ```yaml # 全局数据库连接池配置 spring: datasource: hikari: maximum-pool-size: 20 minimum-idle: 5 idle-timeout: 300000 # 全局日志级别配置 logging: level: root: INFO com.alibaba.nacos: WARN ``` 3. 点击「发布」,完成全局配置创建。 #### 1.2 步骤 2:微服务加载全局配置 微服务通过 `application.yaml`(或 `bootstrap.yaml`)配置,主动加载上述全局 Data ID 的配置,支持两种语法: ##### 语法 1:`shared-configs`(推荐,支持 Group、Namespace) 适用于需要指定 Group 或 Namespace 的场景,配置示例: ```yaml spring: cloud: nacos: config: server-addr: 127.0.0.1:8848 # Nacos 服务地址 namespace: dev # 与全局配置的 Namespace 一致 file-extension: yaml # 配置格式(与全局配置一致) # 加载全局共享配置 shared-configs: - data-id: global-public.yaml # 全局配置的 Data ID group: GLOBAL_GROUP # 全局配置的 Group refresh: true # 是否支持动态刷新(修改后服务自动生效) ``` ##### 语法 2:`shared-dataids`(简化版,仅支持 DEFAULT_GROUP) 仅适用于全局配置在 `DEFAULT_GROUP` 的场景,配置示例: ```yaml spring: cloud: nacos: config: server-addr: 127.0.0.1:8848 namespace: dev file-extension: yaml # 多个共享 Data ID 用逗号分隔 shared-dataids: global-public.yaml # 指定需要动态刷新的 Data ID(逗号分隔) refreshable-dataids: global-public.yaml ``` ### 方案 2:基于「配置分组(Group)+ 通配符」实现按规则共享 适用于**部分服务需要共享配置**的场景(如 “所有支付相关服务共享支付网关配置”),核心思路是:将同类服务的配置归为同一 Group,通过 `group` 或 `data-id-pattern` 匹配加载。 #### 2.1 步骤 1:按规则创建全局配置 例如,为所有支付服务创建全局配置: - Data ID:`pay-global.yaml`(标识 “支付服务全局配置”); - Group:`PAY_GROUP`(支付服务专属分组); - 配置内容:支付网关地址、签名密钥等公共配置: ```yaml pay: gateway: url: http://pay-gateway:8080 sign: secret: abc123456789xyz ``` #### 2.2 步骤 2:服务按 Group 加载配置 支付相关服务(如 `pay-order`、`pay-refund`)在配置中指定 Group 为 `PAY_GROUP`,即可加载该分组下的全局配置: ```yaml spring: cloud: nacos: config: server-addr: 127.0.0.1:8848 namespace: dev file-extension: yaml group: PAY_GROUP # 服务指定 Group,加载该分组下的全局配置 # 同时加载自身服务配置(Data ID 为 pay-order-dev.yaml) data-id: ${spring.application.name}-${spring.profiles.active}.${file-extension} ``` #### 2.3 进阶:基于 `data-id-pattern` 实现动态匹配 若需更灵活的匹配规则(如 “所有以 `pay-` 开头的服务加载全局配置”),可使用 `data-id-pattern`(需 Nacos Client 2.0+): ```yaml spring: cloud: nacos: config: server-addr: 127.0.0.1:8848 namespace: dev file-extension: yaml # 通配符匹配 Data ID:所有以 "pay-" 开头的配置 data-id-pattern: pay-*.yaml group: PAY_GROUP refresh: true ``` ## 三、全局配置的优先级 当全局配置与服务自身配置(如 `serviceA-dev.yaml`)存在相同配置项时,Nacos 遵循 **“服务自身配置优先级高于全局配置”** 的规则,确保服务可覆盖全局配置(满足个性化需求)。 优先级从高到低排序如下: 1. 服务自身配置(`{服务名}-{环境}.{后缀}`,如 `serviceA-dev.yaml`); 2. 全局共享配置(`shared-configs` 或 `data-id-pattern` 加载的配置); 3. Nacos 内置默认配置(极少使用)。 ## 四、全局配置的动态刷新 Nacos 支持全局配置的**动态刷新**(无需重启服务),实现步骤如下: 1. **配置层面**:在微服务的 `application.yaml` 中,为全局配置的 Data ID 设置 `refresh: true`(参考方案 1.2 的 `shared-configs` 配置); 2. 代码层面 在需要感知配置变化的类上添加 ``` @RefreshScope ``` 注解(Spring Cloud 注解),示例: ```java import org.springframework.beans.factory.annotation.Value; import org.springframework.cloud.context.config.annotation.RefreshScope; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RefreshScope // 开启配置动态刷新 public class GlobalConfigController { // 注入全局配置中的支付网关地址 @Value("${pay.gateway.url:default}") private String payGatewayUrl; @GetMapping("/pay/gateway") public String getPayGatewayUrl() { return payGatewayUrl; // 配置修改后,返回值会自动更新 } } ``` ## 五、全局配置的最佳实践 1. **命名规范统一**: - Data ID:建议格式 `{类型}-{用途}.{后缀}`(如 `global-public.yaml`、`pay-global.yaml`); - Group:全局配置用 `GLOBAL_GROUP`,业务分组用 `{业务名}_GROUP`(如 `PAY_GROUP`); - Namespace:按环境划分(`dev`/`test`/`prod`),避免跨环境配置污染。 2. **配置分层管理**: - 全局层(`global-public.yaml`):存放所有服务共享的配置(如注册中心、日志); - 业务层(`pay-global.yaml`):存放某类业务服务共享的配置(如支付、订单); - 服务层(`serviceA-dev.yaml`):存放单个服务的个性化配置(如端口、服务名)。 3. **敏感配置加密**: - 全局配置中的敏感信息(如数据库密码、签名密钥)需加密存储,可结合 Nacos 自带的 **AES 加密** 或第三方加密组件(如 Spring Cloud Config Server 加密)。 4. **版本控制与回滚**: - Nacos 控制台支持配置的**历史版本查看**和**回滚**,修改全局配置前建议备份,避免误操作导致全服务故障。 通过上述方式,可高效实现 Nacos 全局配置的管理,大幅提升微服务配置的复用性和可维护性。 # 参考 ## 微服务拆分 以下是电商微服务拆分的最佳实践: - 遵循拆分原则 - **按业务边界拆分**:根据电商业务流程中自然形成的划分,识别不同的业务域,如用户管理、商品管理、订单管理、支付管理等,每个业务域作为一个独立的微服务,负责该业务域内的所有功能和数据,这样服务职责明确,减少服务间的依赖和耦合。 - **单一职责原则**:每个微服务只负责一项核心业务功能。例如,订单的创建、查询、修改等功能属于订单管理的核心职责,可整合到订单微服务中,而支付功能应单独作为支付微服务,避免微服务职责模糊。 - **数据自治原则**:每个微服务拥有自己独立的数据库,负责管理自身的数据,不允许其他微服务直接访问其数据库。比如用户微服务有自己的用户数据库,商品微服务有商品数据库,服务间通过接口调用获取数据,保证数据的一致性和安全性。 - **服务粒度适度原则**:服务粒度应以能够独立完成一项相对完整的业务功能为标准。服务粒度过大无法发挥微服务的优势,过小则会增加服务间的通信成本和管理难度。 - **演进式拆分原则**:微服务拆分是一个持续演进的过程,企业可根据业务的发展和技术的进步,逐步对微服务进行拆分和重组。可采用增量式的拆分方式,先对部分业务进行拆分试点,再逐步推广到整个系统。 - **采用领域驱动设计(DDD)**:像 ZKmall 开源商城一样,运用 DDD 的思路按业务边界划分服务。通过识别核心子域、划分限界上下文和定义上下文映射关系,明确每个服务的 “责任田”。例如拆出用户中心服务、商品服务、订单服务等,每个服务都有明确的职责,并且通过事件驱动的方式解决跨服务的数据同步问题。 - **合理处理分布式事务**:在电商微服务中,分布式事务是一个关键问题。可采用 Saga 模式结合本地消息表等方式来处理。例如在支付场景中,“支付成功” 和 “扣库存” 需要保证同时成功或同时失败,使用 TCC 模式先试着扣库存、冻金额,再进行确认或回滚操作,确保数据的一致性。 - **实现服务间通信与解耦**:微服务之间可通过 RESTful API 进行同步通信,同时使用消息队列如 RabbitMQ 或 Kafka 来实现异步通信。例如支付模块在支付成功后发送消息通知订单模块更新订单状态,降低服务间的耦合度,提高系统的可扩展性和性能。 - **构建自动化运维体系**:随着微服务数量的增加,部署和运维的成本会显著提升。因此需要构建自动化的工具及环境,包括服务注册与发现、配置中心、监控与日志等。使用 Nacos 进行服务注册与发现和统一配置管理,利用 Prometheus 和 Grafana 监控微服务的性能指标,使用 ELK Stack 进行日志管理,简化开发、测试、部署和运维的重复性工作。 ## 领域驱动设计(DDD) 领域驱动设计(Domain-Driven Design,DDD)是一种针对复杂复杂业务领域、设计高质量软件系统的方法论,核心是将业务领域的知识转化为软件设计,使系统更贴合业务本质,尤其适合复杂业务场景(如电商、金融等)。 ### **DDD 核心概念** 1. **领域(Domain)**指业务所涉及的范围和规则,是系统要解决的核心问题领域(如电商领域包含商品、订单、支付等子领域)。 2. **限界上下文(Bounded Context)** - 领域中具有明确边界的子领域,内部有统一的术语(**通用语言**)和业务规则,外部通过接口交互。 - 例:电商中 “订单上下文” 和 “库存上下文” 是两个限界上下文,分别处理订单流程和库存管理,上下文间通过接口通信。 3. **通用语言(Ubiquitous Language)**开发团队与业务人员共同使用的统一术语,确保代码与业务理解一致。例如在订单上下文,“下单”“支付”“取消” 等术语需在文档、代码、沟通中保持统一。 4. **实体(Entity)**具有唯一标识且状态会变化的对象,如 “订单”(有订单 ID,状态会从 “待支付” 变为 “已完成”)。 5. **值对象(Value Object)**无唯一标识、不可变的对象,用于描述事物的属性,如 “地址”(由省、市、街道组成,无独立 ID)、“金额”(含数值和币种)。 6. **聚合(Aggregate)**一组关联的实体和值对象的集合,作为数据修改和事务的最小单元,由**聚合根**(Aggregate Root)统一对外暴露接口。 - 例:“订单” 是聚合根,包含 “订单项”(实体)、“收货地址”(值对象)等,外部只能通过订单 ID 操作整个聚合。 7. **领域服务(Domain Service)**当业务逻辑涉及多个实体 / 聚合协作时,将逻辑封装为领域服务,如 “订单支付” 需调用订单、支付、库存等多个聚合,可封装为`OrderPaymentService`。 8. **领域事件(Domain Event)**领域中发生的重要事件(如 “订单支付成功”),用于跨上下文通信,实现解耦。例如支付成功后发布事件,通知库存上下文扣减库存。 9. **仓储(Repository)**封装数据访问逻辑,提供对聚合的持久化操作(如`OrderRepository`负责订单的保存、查询),隔离领域模型与数据存储细节。 ### **DDD 设计流程** 1. **事件风暴(Event Storming)**团队通过梳理业务事件、命令、实体等,识别限界上下文和聚合,是 DDD 落地的常用实践。 2. **划分限界上下文**根据业务边界拆分系统,每个上下文对应一个微服务(或模块),确保高内聚低耦合。 3. **设计领域模型**在每个上下文内,识别实体、值对象、聚合,定义领域服务和事件,用通用语言描述业务规则。 4. **实现与集成**基于领域模型编码,通过上下文映射(Context Mapping)处理跨上下文交互(如通过 API、事件总线)。 ### **DDD 的价值** - **业务与技术对齐**:通过通用语言和领域模型,减少业务与开发的理解偏差。 - **应对复杂性**:将大领域拆分为限界上下文,降低系统复杂度。 - **支持微服务拆分**:限界上下文天然适合作为微服务的拆分边界,使服务职责清晰。 - **提高可维护性**:领域模型贴近业务,便于业务变更时的系统迭代。 ### **适用场景** DDD 更适合业务复杂、迭代频繁的系统(如电商平台、金融核心系统)。对于简单 CRUD 场景(如内部管理系统),过度使用 DDD 可能增加设计成本。 通过 DDD,团队能构建出更贴合业务、更易维护的系统,尤其在微服务架构中,能有效解决服务拆分混乱、业务逻辑分散等问题。 ## spu sku 在电商运营和商品管理中,SPU、SKU 是核心基础术语,直接影响商品上架、库存管理、搜索推荐及用户购物体验。此外,还涉及多个关联术语,以下将从 **核心概念(SPU/SKU)**、**关联术语**、**实战应用场景** 三方面详细解析,帮助理解其逻辑关系与实际价值。 ### 一、核心术语:SPU 与 SKU(最基础且易混淆) SPU 和 SKU 的本质区别是 **“抽象商品” 与 “具体商品”** 的关系,前者是 “商品模板”,后者是 “可直接售卖的最小单元”,二者是包含与被包含的逻辑。 | 维度 | SPU(Standard Product Unit,标准产品单元) | SKU(Stock Keeping Unit,库存保有单元) | | ---------------------- | -------------------------------------------------- | ---------------------------------------------------------- | | **定义** | 同一类商品的 “抽象集合”,代表商品的 “基本款” | 商品的 “最小售卖 / 库存单元”,是具体可发货的商品 | | **核心作用** | 归类商品、统一商品信息(如名称、参数) | 区分规格、管理库存、精准履约 | | **是否对应库存** | 不直接对应库存(无具体规格,无法备货) | 直接对应库存(每个 SKU 有独立库存数量) | | **用户感知** | 购物时先看到的 “商品标题主体”(如 “苹果 16 手机”) | 选择规格后确定的 “具体商品”(如 “苹果 16 256G 黑色 国行”) | | **示例(以手机为例)** | SPU:苹果 iPhone 16(所有规格的统称) | SKU1:苹果 16 256G 黑色 国行SKU2:苹果 16 512G 白色 港版 | ### 二、电商高频关联术语(理解 SPU/SKU 的 “上下游”) 除 SPU 和 SKU 外,以下术语常与二者配合使用,覆盖商品从 “创建” 到 “售卖” 的全链路: #### 1. GSPU(Global SPU,全局标准产品单元) - **定义**:跨平台 / 跨店铺的 “统一商品标识”,比 SPU 范围更广(SPU 可能是单店铺的,GSPU 是品牌 / 平台级的)。 - **作用**:解决 “同品不同名” 问题,比如某品牌 “华为 Mate 70” 在天猫、京东、抖音的店铺中,SPU 名称可能有细微差异,但 GSPU 统一为 “华为 Mate 70 系列”,便于品牌统一管理商品信息、同步营销活动。 #### 2. 商品 ID(Product ID) - **定义**:电商平台给每个 “商品页面” 分配的唯一数字 / 字符标识,分为 **SPU ID** 和 **SKU ID**。 - 作用 : - SPU ID:对应一个商品的 “基础页面”(如 “苹果 16” 的主页面); - SKU ID:对应页面中某个具体规格(如 “256G 黑色”),是订单履约、库存扣减的 “唯一凭证”(比如用户下单后,系统通过 SKU ID 找到对应库存并扣减)。 #### 3. 规格(Specification) - 定义 :区分 SKU 的 “核心维度”,是生成 SKU 的前提,常见规格包括: - 物理属性:尺寸(S/M/L)、颜色(黑 / 白 / 红)、重量(500g/1kg)、材质(棉 / 涤纶); - 功能属性:内存(128G/256G)、配置(标准版 / Pro 版)、口味(原味 / 草莓味)。 - **逻辑关系**:**1 个 SPU + 不同规格组合 = 多个 SKU**(例:SPU “纯棉 T 恤”,规格 “颜色(黑 / 白)+ 尺寸(M/L)”,可生成 2×2=4 个 SKU)。 #### 4. 变体(Variation) - **定义**:同一 SPU 下,因规格不同而产生的 “商品差异集合”,本质是 “SKU 的分组”。 - **用户场景**:在商品详情页,点击 “颜色” 下拉框选择 “黑色”,再点击 “尺寸” 选择 “M 码”,这两个操作就是在 “选择变体”,最终确定一个 SKU。平台会将同一变体组的 SKU 放在同一页面,避免用户重复搜索(比如不用分别搜 “黑色 T 恤 M 码”“白色 T 恤 L 码”)。 #### 5. 库存(Inventory) - **与 SKU 的强关联**:库存永远绑定 SKU,而非 SPU—— 因为 “没有规格的库存是无效的”(比如商家说 “有 100 件苹果 16 库存”,但没说内存 / 颜色,无法发货;必须明确 “苹果 16 256G 黑色 有 50 件库存”)。 - 常见库存类型 : - 可用库存:当前可售卖的数量(用户下单会扣减); - 锁定库存:用户下单但未付款,暂时冻结的数量(超时未付款会释放回可用库存); - 预售库存:提前售卖、未来发货的数量(对应 “预售 SKU”)。 #### 6. 类目(Category) - **定义**:电商平台对商品的 “分类体系”,是 SPU 的 “归属地”,比如 “手机” 属于 “3C 数码 > 手机通讯 > 智能手机” 类目。 - **与 SPU 的关系**:每个 SPU 必须归属一个具体类目,类目决定了 SPU 的 “属性模板”(比如 “服装” 类目会要求填写 “面料、版型”,“家电” 类目要求填写 “功率、能效等级”),而属性模板又会影响规格的设置,最终影响 SKU 的生成。 ### 三、实战应用场景:为什么要区分这些术语? 理解术语的核心是为了落地运营,以下 3 个场景能直观体现其价值: #### 场景 1:商品上架(避免 “重复创建”) - 错误做法:把 “苹果 16 256G 黑色”“苹果 16 512G 白色” 分别创建为 2 个 SPU,导致用户搜索 “苹果 16” 时出现多个重复页面,分散流量。 - 正确做法:创建 1 个 SPU “苹果 16”,在该 SPU 下添加 “内存 + 颜色” 规格,生成多个 SKU,所有 SKU 共享同一个商品详情页(主图、参数、评价),既统一信息,又集中流量。 #### 场景 2:库存管理(避免 “超卖 / 缺货”) - 问题:若用 SPU 管理库存(比如 “苹果 16 总库存 100 件”),可能出现 “256G 已卖完,但系统显示还有库存”,导致用户下单后无法发货(超卖)。 - 解决:按 SKU 分仓管理 —— 给每个 SKU 单独设置库存(如 “256G 黑色 50 件、512G 白色 50 件”),用户下单时直接扣减对应 SKU 库存,精准控制库存数量,减少售后纠纷。 #### 场景 3:营销活动(精准定位商品) - 例:平台做 “手机内存 256G 以上满减” 活动,需筛选出所有 “内存≥256G” 的 SKU—— 若没有 SKU 区分规格,无法精准圈选商品;通过 SKU 的 “规格属性”,可快速筛选符合条件的商品,确保活动规则落地。 ### 总结:核心逻辑链 **类目 → SPU(抽象商品,含基础信息)→ 规格(颜色 / 尺寸等)→ SKU(具体商品,含库存)→ 变体(SKU 分组,供用户选择)**所有术语围绕 “让商品可管理、让用户可找到、让库存可精准控制” 展开,是电商精细化运营的基础 —— 理解这些,就能看懂商品从 “创建” 到 “卖出” 的全流程逻辑。 ## 分布式Token 在分布式系统中,**Gateway(网关)** 作为流量入口,负责统一处理认证授权逻辑,而 **Token(令牌)** 是实现分布式身份认证的核心载体。网关传递 Token 的核心目标是:**统一鉴权、减少重复开发、保障服务安全性**,同时确保 Token 在多服务间的有效流转与验证。 ### 一、核心概念:为什么需要网关传递 Token? 分布式系统由多个微服务组成(如用户服务、订单服务、支付服务),若每个服务都单独处理 Token 验证,会导致: 1. **代码冗余**:每个服务重复开发鉴权逻辑(解析 Token、验证有效性、提取用户信息)。 2. **权限不统一**:不同服务的鉴权规则可能不一致,增加安全风险。 3. **扩展性差**:Token 机制升级(如从 JWT 切换到 OAuth2.0)时,需修改所有服务。 而网关作为 “统一入口”,可拦截所有请求,集中完成 Token 的**接收、验证、传递**,让后端微服务无需关注鉴权细节,仅专注于业务逻辑。 ### 二、Token 传递的完整流程(以 JWT 为例) JWT(JSON Web Token)是分布式系统中最常用的 Token 格式(无状态、可自包含用户信息),结合网关的传递流程如下: #### 关键步骤详解 ##### 步骤 1:客户端携带 Token 发起请求 客户端需在 HTTP 请求的 **Header** 中携带 Token(规范格式),最常用的是 `Authorization` 头,格式为: ```http Authorization: Bearer <你的Token> # "Bearer"为固定前缀,空格后接Token字符串 ``` - 示例:`Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOjEsIm5hbWUiOiJhZG1pbiIsImV4cCI6MTYxNjAwMDAwMH0.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c` ##### 步骤 2:网关验证 Token(核心环节) 网关(如 Spring Cloud Gateway、Kong、APISIX)拦截请求后,执行以下验证逻辑: 1. **提取 Token**:从 `Authorization` 头中解析出 Token(去除`Bearer`前缀)。 2. **基础校验**:检查 Token 是否为空、格式是否合法(如 JWT 需包含`.`分隔的 3 部分:Header.Payload.Signature)。 3. **签名验证**:使用预设的密钥(对称加密)或公钥(非对称加密)验证 Token 签名,防止 Token 被篡改。 4. **有效期校验**:解析 Token 的 Payload(如 JWT 的`exp`字段),判断 Token 是否过期。 5. **权限校验**(可选):根据 Token 中的角色 / 权限信息(如`role`字段),判断当前请求是否有权访问目标资源(如 “普通用户无法访问管理员接口”)。 - 若验证失败,网关直接返回错误响应(如`401 Unauthorized`:Token 无效 / 过期;`403 Forbidden`:权限不足),不转发请求到微服务。 ##### 步骤 3:网关传递 Token / 用户信息到微服务 验证成功后,网关需将 “身份信息” 传递给后端微服务,常见有两种传递方式: | 传递方式 | 实现逻辑 | 优点 | 缺点 | 适用场景 | | ----------------------- | ------------------------------------------------------------ | -------------------------------------- | -------------------------------------------------------- | -------------------------------- | | 1. 直接传递原始 Token | 网关不解析 Token 内容,仅将`Authorization`头原样转发给微服务 | 实现简单,网关无状态 | 微服务需重复解析 / 验证 Token,增加开销 | 微服务数量少、Token 验证逻辑简单 | | 2. 传递解析后的用户信息 | 网关解析 Token,提取核心信息(如`userId`、`username`、`role`),放入新的 HTTP 头(如`X-User-Id`、`X-User-Role`),再转发请求 | 微服务无需解析 Token,直接使用用户信息 | 网关需解析 Token,增加少量开销;需确保新 Header 不被篡改 | 微服务数量多、需频繁使用用户信息 | ##### 步骤 4:微服务处理(可选二次验证) - 若网关传递的是**原始 Token**:微服务需再次执行 Token 验证(与网关逻辑一致),确保请求未绕过网关直接访问微服务(需通过网络隔离限制 “仅网关可访问微服务”)。 - 若网关传递的是**解析后的用户信息**:微服务可直接读取`X-User-Id`等 Header,无需处理 Token;为防止伪造 Header,需配置网关拦截所有请求,**覆盖 / 清除客户端自带的`X-User-Id`等头**(避免客户端篡改身份)。 ### 三、网关的 Token 传递实现(实战示例) 以分布式系统中最常用的两款网关为例,说明具体配置: #### 1. Spring Cloud Gateway(Java 微服务生态) Spring Cloud Gateway 基于 Netty 实现,通过 “过滤器(Filter)” 处理 Token,核心是**自定义 GlobalFilter(全局过滤器)**。 ##### 示例:自定义 Token 验证与传递过滤器 ```java @Component public class TokenAuthFilter implements GlobalFilter, Ordered { // 预设的JWT密钥(实际应从配置中心读取,避免硬编码) private static final String JWT_SECRET = "your-secret-key"; @Override public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { // 1. 提取Token ServerHttpRequest request = exchange.getRequest(); String authHeader = request.getHeaders().getFirst("Authorization"); if (authHeader == null || !authHeader.startsWith("Bearer ")) { return handleUnauthorized(exchange, "Token不存在或格式错误"); } String token = authHeader.substring(7); // 去除"Bearer "前缀 try { // 2. 验证Token(使用JJWT库解析JWT) Claims claims = Jwts.parser() .setSigningKey(JWT_SECRET.getBytes(StandardCharsets.UTF_8)) .parseClaimsJws(token) .getBody(); // 3. 校验有效期 long exp = claims.getExpiration().getTime(); if (System.currentTimeMillis() > exp) { return handleUnauthorized(exchange, "Token已过期"); } // 4. 提取用户信息,传递到微服务(添加新Header) String userId = claims.get("userId", String.class); String username = claims.get("username", String.class); ServerHttpRequest newRequest = request.mutate() .header("X-User-Id", userId) .header("X-Username", username) .build(); // 5. 转发请求到微服务 return chain.filter(exchange.mutate().request(newRequest).build()); } catch (Exception e) { // Token验证失败(签名错误、格式错误等) return handleUnauthorized(exchange, "Token无效:" + e.getMessage()); } } // 处理未授权响应(返回401) private Mono handleUnauthorized(ServerWebExchange exchange, String message) { ServerHttpResponse response = exchange.getResponse(); response.setStatusCode(HttpStatus.UNAUTHORIZED); response.getHeaders().add("Content-Type", "application/json;charset=UTF-8"); String body = "{\"code\":401,\"message\":\"" + message + "\"}"; return response.writeWith(Mono.just(response.bufferFactory().wrap(body.getBytes()))); } // 过滤器优先级(数值越小,优先级越高) @Override public int getOrder() { return -100; // 确保在路由过滤器之前执行 } } ``` ### 四、关键注意事项(避坑指南) 1. **Token 的安全性保障** - **HTTPS 传输**:Token 是身份凭证,必须通过 HTTPS 传输,防止被中间人窃取。 - **避免 Token 泄露**:客户端需安全存储 Token(如后端服务用 Redis 存储刷新 Token,前端用`HttpOnly Cookie`存储 Access Token,防止 XSS 攻击)。 - **短有效期 + 刷新 Token**:Access Token 有效期设为 15-30 分钟,过期后用刷新 Token(Refresh Token)重新获取,减少 Token 泄露风险。 2. **防止 Header 篡改** - 若网关传递解析后的用户信息(如`X-User-Id`),需配置网关**覆盖客户端自带的同名 Header**(避免客户端伪造`X-User-Id=1`冒充管理员)。 - 进阶方案:使用**mTLS(双向 TLS)** 确保网关与微服务间的通信安全,防止中间服务篡改 Header。 3. **Token 的扩展性** - 若系统需支持多端登录(Web、APP、小程序),可在 Token 中添加`clientType`字段,网关根据客户端类型调整鉴权规则(如 APP 端允许更长的 Token 有效期)。 - 若需支持第三方登录(如微信、支付宝),可结合 OAuth2.0/OpenID Connect,网关作为 OAuth2.0 的 Resource Server,验证第三方颁发的 Token。 4. **性能优化** - 网关验证 Token 时,避免频繁查询数据库(如 JWT 是无状态的,无需查库;若用 Redis 存储 Token 黑名单,需确保 Redis 高性能)。 - 对高频请求,可在网关添加 Token 缓存(如缓存解析后的用户信息),减少重复解析开销。 ### 五、总结 网关传递 Token 是分布式系统身份认证的 “标准方案”,核心逻辑是: 1. **集中验证**:网关拦截请求,统一完成 Token 的有效性、权限校验,减少微服务冗余代码。 2. **安全传递**:通过 Header 将 Token 或解析后的用户信息转发给微服务,确保身份信息不被篡改。 3. **灵活扩展**:结合 JWT/OAuth2.0 等标准,支持多场景(多端登录、第三方登录),同时通过 HTTPS、短 Token 有效期保障安全性。 选择 Spring Cloud Gateway 或 Kong 等成熟网关,配合标准 Token 机制,可快速实现分布式系统的统一鉴权。