Lecture08-SpringCloud-综合实战

文章目录

    [toc]

    单词词性音标含义
    integratev./ˈɪntɪɡreɪt/集成(把多个服务与组件组合成一套完整系统)
    end-to-endadj./end tʊ end/端到端(从网关入口到数据库,整条调用链都跑通)
    checkoutn./ˈtʃekaʊt/下单结算(本次实战的业务核心流程)
    tracev./n./treɪs/追踪(顺着一次请求排查它经过了哪些服务)
    verifyv./ˈverɪfaɪ/验证(用实际请求确认整个链路可用)

    Lecture08-SpringCloud-综合实战

    目标:把前 6 讲组件串成一套可跑通的业务系统——用户/商品/订单/库存四个服务,注册到 Nacos,配置走配置中心,Feign 远程调用,网关统一入口,验证整条下单链路端到端可用。

    1、实战目标与架构

    模拟一个下单场景:客户端 → 网关 → 订单服务 →(Feign 调用)用户服务 + 商品服务 + 库存服务。

    project-arch

    服务端口职责
    gateway-service80统一入口,路由转发
    user-service8081用户信息
    product-service8082商品信息与库存扣减
    order-service8083下单:查用户 + 查商品 + 扣库存 + 生成订单

    本次实战不做数据库与真正的事务,用内存 Map 模拟数据,重点验证注册/配置/Feign/网关/负载均衡整套链路。


    2、工程结构

    cloud-parent/                  # 父工程(第 01 讲已建)
    ├── pom.xml                    # dependencyManagement:SC + SCA 版本
    ├── gateway-service/           # 网关(L07)
    ├── user-service/              # 用户服务(L03/L04)
    ├── product-service/           # 商品服务
    └── order-service/             # 订单服务(L05/L06)
    

    每个子服务(除网关)都要有:

    • nacos-discovery 依赖 + Nacos 配置 → 注册
    • (可选)nacos-config 依赖 + spring.config.import → 配置中心

    3、准备工作

    1、启动 Nacos

    cd /usr/local/nacos && sudo sh bin/startup.sh -m standalone    # 先配好鉴权三件套(见第 03 讲);-m standalone 不能省
    

    2、各服务统一配置(示例:product-service)

    server:
      port: 8082
    spring:
      application:
        name: product-service
      cloud:
        nacos:
          server-addr: localhost:8848
    

    每个服务 spring.application.name 是服务名,注册、路由、Feign 都靠它。


    4、编写四个服务

    1、user-service:用户接口

    @RestController
    public class UserController {
    
        // 用 Map 模拟用户表
        private final Map<Long, String> users = Map.of(1L, "张三", 2L, "李四");
    
        @GetMapping("/user/{id}")
        public String getUser(@PathVariable Long id) {
            return users.getOrDefault(id, "未知用户");
        }
    }
    

    2、product-service:商品接口

    @RestController
    public class ProductController {
    
        // 商品 id -> 价格
        private final Map<Long, Double> prices = Map.of(1001L, 99.9, 1002L, 199.0);
    
        @GetMapping("/product/{id}")
        public String getProduct(@PathVariable Long id) {
            return "商品" + id + " 价格=" + prices.getOrDefault(id, 0.0);
        }
    }
    

    3、order-service:远程调用接口(Feign)

    a. 定义两个 FeignClient

    @FeignClient(name = "user-service")
    public interface UserClient {
        @GetMapping("/user/{id}")
        String getUser(@PathVariable("id") Long id);
    }
    
    @FeignClient(name = "product-service")
    public interface ProductClient {
        @GetMapping("/product/{id}")
        String getProduct(@PathVariable("id") Long id);
    }
    

    b. 下单接口(串联整条链路)

    @RestController
    public class OrderController {
    
        @Autowired
        private UserClient userClient;
    
        @Autowired
        private ProductClient productClient;
    
        @GetMapping("/order/create")
        public String createOrder(@RequestParam Long userId,
                                  @RequestParam Long productId) {
            // 1. 查用户
            String user = userClient.getUser(userId);
            // 2. 查商品
            String product = productClient.getProduct(productId);
            // 3. 生成订单(真实项目:扣库存 + 写订单表 + 事务)
            return "下单成功![" + user + "],[" + product + "]";
        }
    }
    

    5、网关接入实战

    1、gateway-service 路由配置

    spring:
      cloud:
        gateway:
          server:
            webflux:
              routes:
                # 只配前端直接访问的服务。
                # user-service / product-service 由 order-service 通过 Feign 内部调用,
                # 前端不直接访问,所以不配网关路由(详见 L07)。
                - id: order-route
                  uri: lb://order-service
                  predicates:
                    - Path=/order/**
    

    验证:直接 curl http://localhost/user/1 会 404——这是正常的,说明 user-service 没有暴露到网关外。


    2、启动顺序与验证

    # 启动顺序(注册中心先起)
    user-service     →  8081
    product-service  →  8082
    order-service    →  8083
    gateway-service  →  80
    
    # 端到端验证:只通过网关访问
    curl "http://localhost/order/create?userId=1&productId=1001"
    # 期望输出:下单成功![张三],[商品1001 价格=99.9]
    

    验证点:Nacos 控制台能看到 4 个服务都已注册;请求从网关进入,经 Feign 转发到 user/product 服务,整条链路返回正确。


    6、负载均衡验证(顺带)

    给 product-service 开两个实例(8082、8092,不同端口),多次通过网关调 /order/create?userId=1&productId=1001,观察 order-service 的日志:它 Feign 调 product-service 时,请求被轮询分到 8082 和 8092 两个实例。

    product-service 不配网关路由,不能直接 curl /product/xxx——这正好验证了"内部服务不暴露到网关"的原则。


    7、课堂练习(分组完成)

    1. 按工程结构搭出 4 个服务,全部注册到 Nacos。
    2. 通过网关访问 /order/create?userId=1&productId=1001,确认端到端可用。(user/product 不配网关路由,直接访问会 404——这是正常的。)
    3. 给 product-service 开双实例,验证负载均衡轮询。
    4. (挑战)把某个服务的配置项(如商品折扣)放进 Nacos 配置中心,用 @RefreshScope 动态生效。

    8、本讲小结

    • 综合实战把 Nacos 注册 + 配置 + Feign 调用 + 负载均衡 + 网关串成一条可跑通的业务链路。
    • 服务名(spring.application.name)贯穿注册、路由、Feign 三处,务必命名规范。
    • 网关 lb://服务名 让前端只认一个入口。
    • 真实项目在此基础上加:数据库、Redis 缓存、Sentinel 限流、Seata 事务(后两讲概念)。

    进阶预告:把"扣库存 + 写订单"放到一起并保证一致,就是 L10 Seata 分布式事务要解决的问题;防止某服务流量突增把系统打垮,就是 L09 Sentinel 流量防护要解决的问题。


    9、本节验证

    • 明确实战目标与四服务架构(gateway / user / product / order)✅
    • 会按工程结构搭建服务并统一配置 ✅
    • 会用 Feign 实现 order 远程调用 user/product 并扣库存 ✅
    • 会配置网关路由,端到端验证下单链路 ✅
    end