大家好,我是你们爱拆 "巨石" 的后端博主~最近总有刚入门的小伙伴问:"单体架构好好的,为啥要搞微服务?Feign 到底咋用才不踩坑?" 今天咱就用 "搬砖" 的逻辑,把分布式、单体、微服务这些知识点捋明白,再手把手教你玩明白 Feign+OkHttp,最后还能学会 "抽 api 模块" 的偷懒技巧,看完直接能落地!
一、先搞懂:我们为啥要从 "单体" 跳到 "分布式"?
咱先从后端人的 "噩梦" 说起 ------单体架构。
你可以把单体架构想象成 "祖传的巨石应用":所有代码(用户、订单、支付)都塞在一个项目里,部署的时候像扛着一块大石头跑。好处是初期简单:改代码不用跨项目,部署只传一个 jar 包,连数据库都只用一个。但一旦业务涨起来,问题就炸了:
- 改个支付模块的小 bug,得把整个 "巨石" 重新编译部署,万一手抖把用户模块搞挂了?直接喜提加班券
- 想给订单模块加台服务器扩容?不行!要扩就得整个项目一起扩,资源全浪费在没必要的模块上

- 团队协作更酸爽:10 个人改一个项目,Git 冲突能从上班解决到下班
这时候分布式架构就登场了 ------ 简单说就是 "把巨石拆成小石块,分开放在不同服务器上"。每个小石块负责一块业务(比如用户服务、订单服务),各自部署、各自扩容,就算订单服务挂了,用户服务照样能跑。
而我们常说的微服务,就是分布式架构里最火的 "玩法":它要求每个服务 "小而精"(比如只管订单相关),服务间通过网络通信,还得有配套的 "管理工具"(比如服务注册、配置中心)------ 这就像给 "小石块" 装了导航和对讲机,让它们能互相配合干活。
二、微服务 "脚手架":框架和技术栈怎么选?
别被 "微服务" 三个字吓住,现在有成熟的框架帮我们搭架子,不用从零造轮子~
1. 主流微服务框架
目前后端圈最常用的是 Spring Cloud 和 Spring Cloud Alibaba(国内企业超爱用):
- Spring Cloud:像 "微服务工具箱",里面有各种小工具(服务注册用 Eureka,网关用 Gateway),但部分组件已经停更了
- Spring Cloud Alibaba:在 Spring Cloud 基础上做了优化,还加了国内常用的组件(比如 Nacos、Sentinel),文档全、支持好,新手优先选它!
2. 必懂的微服务技术栈(按场景选)
给大家列个 "实战版技术栈清单",照着选准没错:
功能场景 | 常用组件 | 一句话解释 |
---|---|---|
服务注册 / 发现 | Nacos | 服务的 "快递柜":所有服务在这备案,要找谁直接查 |
配置中心 | Nacos | 统一管理配置:改数据库地址不用挨个服务改 |
网关 | Spring Cloud Gateway | 服务的 "保安亭":所有请求先在这过滤、路由 |
远程调用 | Feign(今天重点讲) | 服务间的 "快递员":帮你调用其他服务,不用写 Http |
熔断降级 | Sentinel | 服务的 "保险丝":防止一个服务挂了拖垮所有 |
分布式事务 | Seata | 跨服务改数据的 "保证书":要么都成功,要么都回滚 |
## 三、动手实战:从 0 建微服务工程 + Feign 调用
光说不练假把式,咱现在就用 Spring Cloud Alibaba 搭个简单的微服务:比如 "订单服务" 调用 "用户服务" 查用户信息。
1. 第一步:创建父工程(统一管理依赖)
先建个 Maven 父工程(比如叫microservice-demo),在pom.xml里定好 Spring Cloud Alibaba 的版本(避免依赖冲突):
xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.13</version> <!-- 注意和Spring Cloud Alibaba版本匹配 -->
</parent>
<dependencyManagement>
<dependencies>
<!-- Spring Cloud Alibaba 依赖管理 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2021.0.5.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
2. 第二步:创建两个子服务(用户 + 订单)
分别建两个 Spring Boot 子模块:
- user-service(用户服务):提供 "根据用户 ID 查用户" 的接口
- order-service(订单服务):需要调用用户服务的接口
先写用户服务(简单粗暴版)
- 加依赖(在user-service的 pom 里):
xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Nacos服务注册 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
</dependencies>
- 写配置(application.yml):
yaml
server:
port: 8081 # 用户服务端口
spring:
application:
name: user-service # 服务名(重要!Feign要靠这名字调用)
cloud:
nacos:
discovery:
server-addr: localhost:8848 # Nacos地址(本地搭个Nacos就行)
- 写个接口(用户服务的 Controller):
less
@RestController
@RequestMapping("/user")
public class UserController {
// 模拟数据库查询
@GetMapping("/{id}")
public String getUserById(@PathVariable Long id) {
return "用户ID:" + id + ",用户名:张三(来自用户服务)";
}
}
再写订单服务(重点:用 Feign 调用用户服务)
- 加依赖(除了基础依赖,还要加 Feign 的依赖):
xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- Feign依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
</dependencies>
- 启动类加注解(开启 Feign 功能):
less
@SpringBootApplication
@EnableDiscoveryClient // 开启服务注册发现
@EnableFeignClients // 关键!开启Feign
public class OrderServiceApplication {
public static void main(String[] args) {
SpringApplication.run(OrderServiceApplication.class, args);
}
}
- 写 Feign 客户端(核心!相当于 "快递员" 的工作手册):
less
// 注解里的name是要调用的服务名(就是用户服务的spring.application.name)
@FeignClient(name = "user-service")
public interface UserFeignClient {
// 这里的路径、参数要和用户服务的接口完全一致!
@GetMapping("/user/{id}")
String getUserById(@PathVariable("id") Long id);
}
- 写订单服务的 Controller,调用 Feign:
less
@RestController
@RequestMapping("/order")
public class OrderController {
// 注入Feign客户端
@Autowired
private UserFeignClient userFeignClient;
@GetMapping("/{orderId}")
public String getOrderInfo(@PathVariable Long orderId) {
// 1. 查订单信息(这里模拟)
String orderMsg = "订单ID:" + orderId + ",金额:99元";
// 2. 调用用户服务查用户(靠Feign)
String userMsg = userFeignClient.getUserById(1L); // 查ID=1的用户
// 3. 拼接返回
return orderMsg + " | " + userMsg;
}
}
- 订单服务的配置(application.yml):
yaml
server:
port: 8082 # 订单服务端口,别和用户服务冲突
spring:
application:
name: order-service
cloud:
nacos:
discovery:
server-addr: localhost:8848
3. 测试一下!
- 先启动 Nacos(本地启动,访问localhost:8848能看到控制台)
- 启动用户服务、订单服务(看 Nacos 控制台,两个服务都注册成功)
- 访问订单服务的接口:http://localhost:8882/order/1
- 预期返回:订单ID:1,金额:99元 | 用户ID:1,用户名:张三(来自用户服务)
成了!这就是 Feign 的魅力 ------ 不用写 HttpURLConnection、不用拼请求地址,像调用本地方法一样调用远程服务~
四、Feign 优化:换个 "更快的快递车"(OkHttp)
Feign 默认用的是HttpClient,性能一般。咱换个 "更快的快递车"------OkHttp,还能加连接池减少连接开销,提升性能。
优化步骤:
- 加 OkHttp 依赖(在订单服务的 pom 里):
xml
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-okhttp</artifactId>
</dependency>
- 加配置(application.yml里加 Feign 配置):
yaml
feign:
client:
config:
default: # default表示所有Feign客户端都生效,也可以指定具体服务名
connect-timeout: 5000 # 连接超时时间(5秒)
read-timeout: 5000 # 读取超时时间(5秒)
okhttp:
enabled: true # 开启OkHttp
httpclient:
enabled: false # 关闭默认的HttpClient
# OkHttp连接池配置(也可以用配置类,这里用yml更简单)
okhttp:
connection-pool:
max-idle-connections: 200 # 最大空闲连接数
keep-alive-duration: 300000 # 连接保持时间(毫秒,这里5分钟)
- (可选)写个配置类手动配置 OkHttp 连接池(更灵活):
less
@Configuration
@EnableFeignClients
public class FeignConfig {
@Bean
public OkHttpClient okHttpClient() {
return new OkHttpClient.Builder()
.connectionPool(new ConnectionPool(
200, // 最大空闲连接数
5, TimeUnit.MINUTES // 保持时间
))
.connectTimeout(5, TimeUnit.SECONDS)
.readTimeout(5, TimeUnit.SECONDS)
.writeTimeout(5, TimeUnit.SECONDS)
.build();
}
}
这样一改,Feign 调用的性能会明显提升 ------ 尤其是高并发场景下,连接池能减少频繁创建连接的开销,OkHttp 的异步请求也更高效~
五、偷懒技巧:抽取 api 模块,避免重复代码
现在有个问题:如果还有 "支付服务" 也要调用用户服务的/user/{id}接口,难道每个服务都要写一遍UserFeignClient?太冗余了!
解决办法:抽取一个独立的 api 模块,把 Feign 客户端、实体类都放进去,其他服务直接依赖这个模块就行。
步骤:
- 新建一个 Maven 模块(比如叫user-api),不用加 Spring Boot 依赖(纯 Java 模块)
- 在user-api里加 Feign 依赖(只加接口相关的,不用加 starter):
xml
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-openfeign-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<scope>provided</scope> <!-- 只在编译时用,避免依赖冲突 -->
</dependency>
</dependencies>
- 把之前order-service里的UserFeignClient移到user-api里:
less
// 还是这个接口,只是放在了api模块
@FeignClient(name = "user-service")
public interface UserFeignClient {
@GetMapping("/user/{id}")
String getUserById(@PathVariable("id") Long id);
}
- 其他服务(比如订单服务、支付服务)依赖user-api:
xml
<dependency>
<groupId>com.xxx</groupId> <!-- 你的groupId -->
<artifactId>user-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
- 直接用!订单服务里不用再写UserFeignClient,直接注入调用就行(和之前一样)。
这样一来,不管多少服务要调用用户服务,都只需要依赖user-api,改接口也只用改一处 ------ 妥妥的 "一次编写,多处复用"!
总结:微服务学习路径(新手收藏)
- 先理解:单体→分布式→微服务的演进逻辑(为什么要拆)
- 搭环境:本地装 Nacos,跑通两个服务的 Feign 调用(重点练手)
- 优化:加 OkHttp 连接池,解决性能问题
- 规范:抽取 api 模块,避免重复代码
- 进阶:学 Sentinel 熔断、Seata 分布式事务(后续再讲)
最后问大家一个问题:你们在 Feign 调用时有没有遇到过 "服务名写错导致调用失败" 的坑?或者有其他优化小技巧?评论区聊聊~ 觉得有用的话,点赞收藏走一波,下次实战不迷路!