这篇文章摘要主要讲述了在微服务架构中,如何通过服务调用解决blog-info-service迁移时遇到的数据依赖问题。作者首先分析了直接访问数据库的错误方案会导致服务耦合,随后提出了正确的解决方案:使用OpenFeign进行服务间通信,并引入api模块定义服务契约。文章详细阐述了完整的调用链流程,包括Nacos服务发现和Feign远程调用的配合使用,强调了微服务自治的重要性。最后总结了四个关键面试问题,并指出这一节将SpringCloud的各个组件(Gateway、Nacos、Feign等)串联起来,形成完整的知识链。
目录
[4.3.3 blog-info-service 迁移 ------ 第一次真正体现微服务调用](#4.3.3 blog-info-service 迁移 —— 第一次真正体现微服务调用)
[五、这里引入 api 模块](#五、这里引入 api 模块)
[九、这里重新理解 Nacos](#九、这里重新理解 Nacos)
[1. 为什么微服务之间不能直接调用Service?](#1. 为什么微服务之间不能直接调用Service?)
[2. 为什么需要api模块?](#2. 为什么需要api模块?)
[3. Feign为什么需要服务名?](#3. Feign为什么需要服务名?)
[4. Nacos和Feign关系?](#4. Nacos和Feign关系?)
[十一、你现在整个 Spring Cloud 知识链已经串起来了](#十一、你现在整个 Spring Cloud 知识链已经串起来了)
[4.3.4 Gateway 服务迁移](#4.3.4 Gateway 服务迁移)
继续,我们进入:
4.3.3 blog-info-service 迁移 ------ 第一次真正体现微服务调用
这一节比用户服务迁移更重要。
原因:
用户服务迁移只是拆模块。
但是博客服务迁移会遇到真正的微服务问题:
一个服务需要另一个服务的数据怎么办?
这就是:
-
服务间通信
-
OpenFeign
-
API模块
-
Nacos服务发现
真正落地。
一、原来的博客模块
单体项目:
blog-system
controller
BlogController
service
BlogService
mapper
BlogMapper
entity
Blog
调用:
浏览器
↓
BlogController
↓
BlogService
↓
BlogMapper
↓
MySQL
例如:
查询博客列表:
@GetMapping("/list")
public List<Blog> list(){
return blogService.list();
}
但是现在拆成微服务:
spring-cloud-blog
├── user-info-service
│
└── blog-info-service
博客代码全部迁移:
blog-info-service
├── controller
│ BlogController
├── service
│ BlogService
├── mapper
│ BlogMapper
├── entity
│ Blog
└── BlogInfoApplication
二、但是马上出现问题
假设博客列表:
以前返回:
{
"id":1,
"title":"Spring Cloud学习",
"userId":1001
}
现在前端希望:
{
"id":1,
"title":"Spring Cloud学习",
"user":{
"id":1001,
"username":"xiaolu"
}
}
怎么办?
以前:
同一个项目:
直接查:
userMapper.selectById(userId)
即可。
现在:
两个服务:
blog-service
❌
userMapper
不存在。
因为:
user-service:
另外一个 JVM。
三、错误方案:直接访问数据库
有人会想到:
博客服务:
直接连接:
user_db
查询用户。
例如:
select *
from user
where id=1001;
看似简单。
但是问题:
服务耦合。
结构:
blog-service
|
|
↓
user_db
那么:
用户服务存在意义降低。
如果用户表改字段:
例如:
username
改成
nickname
博客服务也要改。
这违反:
微服务自治。
四、正确方案:服务调用
架构:
blog-service
|
|
↓
user-service
通过:
HTTP/RPC。
流程:
用户请求博客列表
↓
gateway
↓
blog-service
↓
调用 user-service
↓
返回用户信息
↓
组装博客数据
↓
返回前端
五、这里引入 api 模块
问题:
blog-service 怎么知道:
user-service 有什么接口?
比如:
查询用户:
User getUserById(Long id);
如果直接写:
@RestController
不行。
因为:
Controller是服务实现。
所以:
抽接口。
创建:
user-info-api
结构:
user-info
├── user-info-api
│
└── user-info-service
user-info-api
只放:
接口。
例如:
public interface UserApi {
UserDTO getById(Long id);
}
注意:
没有:
@Service
没有:
数据库。
只是:
契约。
六、user-info-service实现
真正代码:
java
@RestController
public class UserController
implements UserApi {
@Override
public UserDTO getById(Long id){
return userService.getById(id);
}
}
关系:
接口
UserApi
↑
|
|
UserController
↑
|
UserService
七、blog-service如何调用?
blog-service:
引入:
java
<dependency>
<artifactId>
user-info-api
</artifactId>
</dependency>
然后:
创建Feign接口:
java
@FeignClient(
name="user-service"
)
public interface UserFeignClient
extends UserApi {
}
这一步非常关键。
你之前学:
OpenFeign。
现在终于知道为什么需要它。
八、完整调用链
请求:
GET /blog/list
进入:
Gateway:
localhost:9000
↓
路由:
lb://blog-service
↓
blog-service:
BlogController
↓
查询博客:
BlogService
↓
发现需要用户:
调用:
UserFeignClient
↓
OpenFeign:
根据:
name=user-service
去:
Nacos找。
↓
Nacos返回:
user-service
192.168.1.10:8081
↓
HTTP调用:
↓
UserController
↓
返回。
整体:
Nacos
↑
|
Gateway
|
|
blog-service
|
Feign
|
user-service
九、这里重新理解 Nacos
以前你学:
Nacos注册发现。
感觉:
只是:
服务列表。
现在结合业务:
它解决:
服务地址变化问题。
例如:
用户服务:
今天:
192.168.1.10:8081
明天:
扩容:
192.168.1.11:8081
192.168.1.12:8081
博客服务不用改代码。
因为:
调用:
@FeignClient("user-service")
不是:
http://192.168.1.10:8081
十、这一节面试重点
1. 为什么微服务之间不能直接调用Service?
答:
因为服务运行在不同JVM中,无法共享对象,需要通过RPC或者HTTP通信。
2. 为什么需要api模块?
答:
api模块定义服务契约,实现接口和调用方解耦,Feign可以基于接口生成远程调用代理。
3. Feign为什么需要服务名?
答:
服务名用于Nacos服务发现,通过服务名获取真实实例地址。
4. Nacos和Feign关系?
一句话:
Nacos负责找服务,Feign负责调用服务。
十一、你现在整个 Spring Cloud 知识链已经串起来了
之前:
你学的是零件:
Gateway
JWT
Nacos
Feign
LoadBalancer
现在案例:
把零件组装:
客户端
↓
Gateway
(统一入口 + JWT)
↓
Nacos
(服务发现)
↓
Feign
(远程调用)
↓
user-service/blog-service
↓
数据库
下一节进入:
4.3.4 Gateway 服务迁移
这里会把你之前写的:
AuthGlobalFilter
真正放入案例。
重点:
-
网关如何统一鉴权
-
为什么服务内部还需要校验
-
JWT用户信息如何向下传递
-
Header透传设计
这一部分和你之前练的 Gateway Filter 完全对应。