一、先搞懂:为什么需要微服务?(架构演进)
1. 单体架构:简单但笨重(小项目的 “小商店”
是什么:所有功能(比如用户、订单、支付)都写在一个项目里,打包成一个文件部署。
像街边的小商店,收银、货架、仓库都在一间屋里。
特点:所有功能集中在一个项目,打包成一个部署单元
优点:
✅ 开发简单,部署方便
✅ 适合小型项目(如学生管理系统)
缺点:
❌ 耦合度高,牵一发而动全身:改一个功能可能影响其他功能,团队协作吵架;
❌ 扩展性差,无法按需扩容:比如订单模块崩溃,整个系统都用不了;
❌ 升级麻烦:改一行代码也要重新打包整个项目。
2. 分布式架构:解耦但复杂(中型项目的 “连锁店”
是什么:把单体项目拆成多个独立服务(比如 “用户服务”“订单服务”),每个服务跑在不同服务器上,通过网络调用。
像连锁店,收银店、仓库店分开,但互相配合。
特点:多个独立服务通过网络
通信协作
优点:
✅ 服务解耦,独立部署:一个服务坏了不影响其他,比如订单服务挂了,用户还能登录。
✅ 技术栈灵活,可按服务选择
✅ 扩展性强,可针对性扩容
缺点:
❌ 架构复杂,调试困难:服务太多,不知道该调用哪个服务器的哪个服务;
❌ 需要处理网络通信、数据一致性等问题
3. 微服务:精致的分布式架构(大型项目的 “专业分工”
是什么:比分布式架构更精细的拆分 —— 每个服务只做一件事(比如 “用户登录服务”“订单创建服务”),完全独立(自己的代码、数据库、团队)。
像大公司的部门分工,财务只管钱,人事只管人,互不干涉。
核心特征:
🔍 单一职责:每个服务只做一件事(比如 “订单服务” 只处理订单,不碰用户数据);
🛡️ 自治独立:独立开发、部署、运维(比如订单用 Java,用户用 Python);
🌐 接口通用:标准接口,与技术无关(对外提供标准接口(比如 HTTP),不管内部用什么技术;
🚧 隔离容错:避免级联故障(一个服务崩了,通过 “降级”“容错” 不让其他服务跟着崩。
微服务本质:一种经过良好设计的分布式架构方案,强调高内聚、低耦合
关键工具:SpringCloud
微服务是 “架构思想”,SpringCloud 是 “落地工具”—— 它把微服务需要的功能(注册服务、发现服务、负载均衡)都打包好了,拿来就能用(基于 SpringBoot,小白友好)。
SpringCloud:微服务一站式解决方案
核心组件概览
组件 | 功能 | 说明 |
---|---|---|
Eureka | 服务注册与发现 | 服务治理核心 |
Ribbon | 客户端负载均衡 | 服务调用分配 |
Feign | 声明式服务调用 | 简化HTTP客户端 |
Hystrix | 服务容错保护 | 防止雪崩效应 |
Zuul/Gateway | API网关 | 统一入口管理 |
二、微服务第一步:怎么拆分服务?
拆分服务不能乱拆,要遵守 3 个原则,不然越拆越乱:
原则 | 说明 | 示例 |
---|---|---|
单一职责 | 每个服务专注一个业务领域 | 用户服务、订单服务、支付服务 |
数据独立 | 服务间不直接访问数据库,每个服务有自己的数据库,不碰别人的数据库 | 订单服务只操作 “订单库”,要查用户数据只能调用 “用户服务” 的接口 |
接口通信 | 通过API进行服务间调用 | 用户服务暴露 “根据 ID 查用户” 的接口,供订单服务调用(如RESTful API、gRPC |
拆分策略
拆分时机:
🚀 创业项目:先单体,后微服务
🏢 大型项目:直接微服务,避免后期拆分成本
实战示例:订单 + 用户服务拆分
以 “电商下单” 为例,拆成两个服务:
- 订单服务(order-service):管下单、查订单,数据库是 “订单库”;
- 用户服务(user-service):管登录、查用户信息,数据库是 “用户库”。
关键规则:订单服务要查用户信息,不能直接读 “用户库”,必须调用用户服务的接口(比如http://用户服务地址/user/123)。
项目结构
cloud-demo/
├── user-service/ # 用户服务
│ ├── src/
│ └── pom.xml
├── order-service/ # 订单服务
│ ├── src/
│ └── pom.xml
└── pom.xml # 父工程
三、服务间怎么调用?用 RestTemplate
服务拆分后,订单服务要调用用户服务,怎么发网络请求?Spring 给了个工具:RestTemplate,2步搞定:
步骤 1:在订单服务里注册 RestTemplate
在订单服务的启动类里加一段代码,把 RestTemplate 交给 Spring 管理:
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class, args);
}
// 注册RestTemplate,用于发HTTP请求
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
步骤 2:订单服务调用用户服务接口
在订单服务的业务代码里,用 RestTemplate 发请求,获取用户信息:
@Service
public class OrderService {
@Autowired
private RestTemplate restTemplate; // 注入RestTemplate
public Order queryOrderById(Long orderId) {
// 1. 查自己的订单数据(从订单库查)
Order order = orderMapper.findById(orderId);
// 2. 调用用户服务,查用户信息
String userUrl = "http://localhost:8081/user/" + order.getUserId();
// 发GET请求,把返回结果转成User对象
User user = restTemplate.getForObject(userUrl, User.class);
// 3. 把用户信息放进订单里,返回
order.setUser(user);
return order;
}
}
注意:http://localhost:8081
是用户服务的地址,/user/123是用户服务的接口。
四、问题来了:服务多了怎么办?用 Eureka 注册中心
如果用户服务部署了多个实例
(比如8081和8082端口),订单服务会遇到 3 个难题:
- 不知道用户服务的地址是8081还是8082;
- 不知道该选哪个实例调用;
- 不知道某个实例是不是已经崩了。
这时候需要Eureka 注册中心—— 相当于 “服务通讯录”,所有服务都在这里登记自己的地址,需要调用时来这里查。
1. Eureka 怎么工作?(3 步流程)
- 服务注册:用户服务启动后,主动把自己的地址(如8081)写到 Eureka 的 “通讯录” 里;
- 服务发现:订单服务需要调用用户服务时,去 Eureka 查 “用户服务” 对应的所有地址(8081、8082);
- 健康检查:用户服务每隔 30 秒给 Eureka 发 “心跳”,没发心跳的实例会被 Eureka 从通讯录里删掉,避免订单服务调用崩掉的实例。
Eureka架构解析
Eureka核心机制
机制 | 说明 | 默认值 |
---|---|---|
服务注册 | 服务启动时向Eureka注册 | 30秒内完成 |
服务续约 | 定期发送心跳保持活跃 | 每30秒一次 |
服务发现 | 消费者从Eureka获取服务列表 | 每30秒更新 |
服务下线 | 服务关闭时通知Eureka | 主动通知 |
失效剔除 | Eureka清理无心跳服务 | 90秒无心跳 |
2. 搭建 Eureka 注册中心(4 步)
步骤 1:创建 Eureka 服务(独立项目)
在父工程下新建子模块eureka-server
,专门当注册中心。
步骤 2:引入 Eureka 依赖
在pom.xml里加依赖(SpringCloud 提供的 Eureka 服务端依赖):
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
步骤 3:写启动类(加 @EnableEurekaServer)
@SpringBootApplication
@EnableEurekaServer // 开启Eureka注册中心功能
public class EurekaApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaApplication.class, args);
}
}
步骤 4:写配置文件(application.yml)
server:
port: 10086 # Eureka的端口(自己定,比如10086)
spring:
application:
name: eureka-server # 服务名
eureka:
client:
service-url:
# Eureka自己也是服务,默认会注册自己(这里先让它注册到自己)
defaultZone: http://127.0.0.1:10086/eureka
启动 Eureka 后,浏览器访问http://127.0.0.1:10086
,能看到 Eureka 的管理页面,说明搭建成功!
3. 把服务注册到 Eureka(以用户服务为例)
步骤 1:加 Eureka 客户端依赖
在用户服务的pom.xml里加依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
步骤 2:改配置文件(告诉 Eureka 地址)
spring:
application:
name: userservice # 服务名(重要!订单服务会用这个名查)
eureka:
client:
service-url:
# 告诉Eureka注册中心的地址
defaultZone: http://127.0.0.1:10086/eureka
启动用户服务,刷新 Eureka 页面,能看到userservice出现在 “服务列表” 里,注册成功!
4. 订单服务用 Eureka 查地址(服务发现)
步骤 1:和用户服务一样,加 Eureka 依赖、改配置文件(略)。
步骤 2:给 RestTemplate 加 @LoadBalanced
(实现负载均衡)
在订单服务的启动类里,给 RestTemplate 加注解,让它能从 Eureka 查地址,还能自动选实例(比如轮流调用8081和8082):
@Bean
@LoadBalanced // 关键:开启负载均衡,能通过服务名查地址
public RestTemplate restTemplate() {
return new RestTemplate();
}
步骤 3:用服务名代替 IP 地址
原来调用用户服务的地址是http://localhost:8081/user/123,现在改成服务名userservice:
// 原来的地址(硬编码IP,不好)
String userUrl = "http://localhost:8081/user/" + order.getUserId();
// 改成服务名(Eureka会自动转成具体IP)
String userUrl = "http://userservice/user/" + order.getUserId();
启动订单服务,调用接口时,会自动从 Eureka 查userservice的所有实例,轮流调用(负载均衡),再也不用管具体 IP 了!
最佳实践与常见问题
多实例启动技巧
为了演示一个服务有多个实例的场景,我们添加一个SpringBoot的启动配置,再启动一个user-service。
- 首先,复制原来的user-service启动配置:Services模块,选中user-service模块,右键–>Copy Configuration…
- 然后,在弹出的窗口中,填写信息:Configuration–>VM options:-Dserver.port=xxxx
- 启动两个user-service实例
常见问题解决方案
1. 服务无法注册
检查Eureka Server地址配置
验证网络连通性
2. 服务发现失败
确认服务名称是否正确
检查@LoadBalanced注解
3. 调用超时
调整Ribbon超时配置
检查服务提供者状态
总结与进阶
本篇重点
✅ 微服务架构演进与优势
✅ 服务拆分原则与实践
✅ Eureka注册中心原理与搭建
✅ 服务注册与发现实战
微服务不是银弹,选择合适的架构比追求技术潮流更重要!从单体到微服务是一个渐进过程,建议根据业务实际需求谨慎决策。