SpringCloud02-服务拆分&远程调用

一、服务的拆分

1-1、springcloud项目的标准结构

SpringCloud 项目 ≠ 一个项目,而是一组项目(多个 SpringBoot 服务)。

这组项目通常包含:

  • 网关

  • 注册中心

  • 配置中心

  • 业务服务(如用户/订单/支付)

  • 公共依赖模块


标准项目结构:

复制代码
cloud-project/               ← 父工程(管理所有模块)
│── pom.xml                  ← 统一版本管理(dependencyManagement)
│
├── cloud-common/            ← 公共模块(工具类/统一响应/异常处理)
│   └── pom.xml
│
├── cloud-api/               ← 公共 Feign 接口模块(服务间调用用)
│   └── pom.xml
│
├── cloud-gateway/           ← 服务网关模块(统一入口)
│   └── pom.xml
│
├── cloud-register/          ← 注册中心(Nacos / Eureka)
│   └── pom.xml
│
├── cloud-config/            ← 配置中心(Nacos Config / SpringCloud Config)
│   └── pom.xml
│
├── cloud-user-service/      ← 用户服务(业务服务 A)
│   └── pom.xml
│
├── cloud-order-service/     ← 订单服务(业务服务 B)
│   └── pom.xml
│
└── cloud-pay-service/       ← 支付服务(业务服务 C)
    └── pom.xml

各模块用来干嘛?

模块 作用 你可以理解为
cloud-project(父工程) 管理所有子模块的版本和依赖 整个系统的大外框
cloud-common 公共工具类、返回结果封装、异常处理 公共工具箱
cloud-api Feign 接口、服务之间的调用客户端 服务之间的通讯协议
cloud-gateway 统一入口、权限验证、路由、限流 大门/前台
cloud-register 服务注册与发现(Nacos/Eureka) 服务通讯录
cloud-config 集中配置管理 公司制度手册
cloud-user-service 用户业务逻辑 工人 A
cloud-order-service 订单业务逻辑 工人 B
cloud-pay-service 支付业务逻辑 工人 C

1-2、项目从零启动的顺序

  1. 先启动注册中心(Nacos/Eureka)

  2. 再启动配置中心(如果有)

  3. 再启动网关

  4. 最后启动每个业务服务

为什么?

因为业务服务启动时需要去「注册中心」登记自己。

二、微服务的远程调用

2-1、DEMO项目结构

2-2、RestTemplate 发起http请求

2-3、Feign

1、微服务之间的远程调用有两种方式

调用方式 技术 特点
基于 HTTP OpenFeign(最常用) 写接口就能调用,简单优雅
基于 RPC Dubbo / gRPC / Thrift 二进制协议,性能更高,但更复杂

SpringCloud 主推的是 OpenFeign


2、场景举例

订单服务 → 想要查询用户信息 → 调用用户服务的接口

复制代码
OrderService ----(网络调用)----> UserService

3、开始用 Feign 调用

第一步:在服务中开启 Feign

在订单服务的启动类上加:

java 复制代码
@EnableFeignClients
@SpringBootApplication
public class OrderApplication {}

第二步:在 pom.xml 中引入依赖
XML 复制代码
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

第三步:定义一个 Feign 接口
java 复制代码
@FeignClient(name = "user-service") // 指向注册中心里的服务名
public interface UserClient {

    @GetMapping("/user/{id}")
    UserDTO getUserById(@PathVariable("id") Long id);

}

注意:

部分 意义
@FeignClient(name="user-service") 告诉 Feign 去调用 注册中心中名字叫 user-service 的服务
@GetMapping("/user/{id}") 告诉它调用对方的这个 HTTP 接口

【注意】:

这是被调用的接口!!!


第四步:在订单服务里调用它(像本地方法一样简单)
java 复制代码
@Service
public class OrderService {

    @Autowired
    private UserClient userClient;

    public void createOrder(Long userId) {
        // 像本地方法一样,直接调用
        UserDTO user = userClient.getUserById(userId);
        System.out.println("下单用户:" + user.getName());
    }
}

虽然看起来像本地方法调用,但本质是 HTTP 远程调用。


2-4、Feign + 负载均衡

如果 user-service 启动多份实例:

复制代码
user-service:8081
user-service:8082
user-service:8083

Feign 会 自动做负载均衡,因为它与 Ribbon / LoadBalancer 集成。

你不需要写代码,SpringCloud 会帮你自动轮询调用不同实例。


2-5、Feign + 熔断(服务挂掉怎么办?)

如果用户服务挂了,为避免订单服务崩溃,你可以加 fallback:

java 复制代码
@FeignClient(name = "user-service", fallback = UserClientFallback.class)
public interface UserClient { ... }

@Component
public class UserClientFallback implements UserClient {
    public UserDTO getUserById(Long id) {
        return new UserDTO(-1L, "用户服务当前不可用");
    }
}

这叫 服务降级保护


小结

在 SpringCloud 中,微服务之间通过 OpenFeign 实现远程调用。

Feign 会根据 @FeignClient 指定的服务名,从 注册中心(Nacos/Eureka) 获取服务实例地址,并通过 HTTP 完成调用。

同时结合 Ribbon / LoadBalancer 实现负载均衡,结合 Sentinel/Hystrix 提供熔断与降级保护,避免服务雪崩。


一句话总结

OpenFeign = 写接口 → 自动发 HTTP 请求 + 自动负载均衡 + 可降级保护。


三、RestTemplate 和 Feign 是什么关系

技术 出现时间 作用 写代码体验
RestTemplate 老一辈 手写 URL,自行发 HTTP 请求 麻烦,自己组装参数
OpenFeign 新一代(推荐) 写接口就能调用,帮你自动 HTTP 请求 简单优雅

Feign 内部本质就是用 RestTemplate(或 HttpClient)发请求,只是帮你封装好了


3-1、代码演变过程

1)初级(无注册中心)

订单服务直接写死用户服务的 IP:

java 复制代码
RestTemplate rt = new RestTemplate();
rt.getForObject("http://localhost:8081/user/1", User.class);

缺点:写死了 IP + 端口,服务迁移/扩容就挂了。


2)中级(有注册中心 + 负载均衡 Ribbon)

java 复制代码
@Autowired
RestTemplate rt;

// 加上 @LoadBalanced,名字会去注册中心找
User u = rt.getForObject("http://user-service/user/1", User.class);

✔ 不再写死 IP

✔ 可以自动轮询多实例

✖ 但是 URL 写法仍然比较"硬"


3)高级(Feign)

java 复制代码
@FeignClient("user-service")
public interface UserClient {

    @GetMapping("/user/{id}")
    User getUser(@PathVariable Long id);
}

使用时:

复制代码
userClient.getUser(id);

像本地方法一样用 → 最优雅

相关推荐
论迹5 小时前
【Spring Cloud微服务】-- DependencyManagement 和 Dependencies
spring·spring cloud·微服务
ruleslol10 小时前
SpringCloud01-初识微服务&SpringCloud
spring cloud
论迹2 天前
【Spring Cloud 微服务】-- 服务拆分原则
java·spring cloud·微服务
灰小猿2 天前
Spring前后端分离项目时间格式转换问题全局配置解决
java·前端·后端·spring·spring cloud
黄暄2 天前
微服务面试题(14题)
java·spring cloud·微服务·架构·java-rabbitmq·java-zookeeper
编啊编程啊程3 天前
【029】智能停车计费系统
java·数据库·spring boot·spring·spring cloud·kafka
我要去腾讯3 天前
Springcloud核心组件之Sentinel详解
java·spring cloud·sentinel
许心月4 天前
坑#Spring Cloud Gateway#DataBufferLimitException
spring cloud
suuijbd4 天前
SpringCloud+Netty集群即时通讯项目
spring boot·分布式·spring cloud·java-rabbitmq·java-zookeeper