跳到主要内容微服务架构:从入门到进阶指南 | 极客日志Javajava
微服务架构:从入门到进阶指南
介绍微服务架构的核心概念、优势及与单体架构的对比。涵盖服务注册发现(Nacos)、远程调用(OpenFeign)、负载均衡、API 网关、配置中心、熔断限流(Sentinel)、分布式事务(Seata)、链路追踪(SkyWalking)及消息队列等关键组件。提供集成示例、最佳实践及避坑指南,帮助开发者构建高可用、可扩展的微服务系统。
FlinkHero27 浏览 微服务架构:从入门到进阶指南
一、为什么需要微服务?
1.1 从一个真实痛点说起
想象一个电商系统,所有功能都写在一个项目里:用户模块、订单模块、商品模块、支付模块……
随着业务增长,问题开始浮现:
- 部署风险极高:改一个支付 Bug,整个系统都要重新部署
- 扩展困难:订单模块压力大,但没法单独扩容,只能整体扩
- 技术栈被锁死:想在某个模块用 Go 提升性能?不可能
- 团队协作灾难:100 人同时改同一个项目,合并代码就是噩梦
- 启动慢如蜗牛:本地跑个项目要等 3 分钟
这就是单体架构的天花板,而微服务正是为了打破这个天花板而生。
二、什么是微服务?
2.1 官方定义
微服务(Microservices)是一种将单一应用程序拆分为一组小型、独立、可独立部署的服务的架构风格。每个服务运行在自己的进程中,通过轻量级通信机制(通常是 HTTP/REST 或消息队列)进行交互。
2.2 用人话解释
把大象切成小块,每块独立运作:
电商系统(单体) 电商系统(微服务)
┌─────────────────────┐ ┌──────────┐ ┌──────────┐
│ │ │ 用户服务 │ │ 订单服务 │
│ 用户 + 订单 + 商品 │ → └──────────┘ └──────────┘
│ + 支付 + 物流 + … │ ┌──────────┐ ┌──────────┐
└─────────────────────┘ │ 商品服务 │ │ 支付服务 │
└──────────┘ └──────────┘
每个服务:
- 有独立的代码仓库
- 有独立的数据库
- 可以独立部署、独立扩容
- 可以用不同的技术栈开发
2.3 微服务的核心原则
| 原则 | 说明 |
|---|
| 单一职责 | 每个服务只做一件事,做好一件事 |
| 自治性 | 服务间松耦合,独立开发、独立部署 |
| 去中心化 | 数据管理去中心化,每服务拥有自己的数据 |
| 容错设计 | 任何服务都可能失败,系统必须能优雅降级 |
| 可观测性 | 日志、指标、链路追踪缺一不可 |
三、单体 vs 微服务对比
| 维度 | 单体架构 | 微服务架构 |
|---|
| 开发效率 | 初期快,后期慢 | 初期慢,后期快 |
| 部署 | 整体部署,风险高 | 独立部署,风险低 |
|
注意:微服务不是银弹!小团队、简单业务用微服务是过度设计,会带来巨大的运维负担。
四、微服务核心组件全景图
┌─────────────────────────────────────────────┐
│ 客户端 │
└─────────────────────┬───────────────────────┘
│
┌─────────────────────▼───────────────────────┐
│ API 网关 (Gateway) │
│ 路由 / 鉴权 / 限流 / 熔断 / 灰度发布 │
└──────┬──────────────┬──────────────┬────────┘
│ │ │
┌──────▼──────┐ ┌────▼─────┐ ┌──────▼───────────┐
│ 用户服务 │ │ 订单服务 │ │ 商品服务 │
│ (user-svc) │ │(order-svc)│ │(goods-svc) │
└───────┬─────┘ └────┬─────┘ └───┬───────────┘
│ │ │
┌───────▼────────────────────▼───────────────▼───────────┐
│ 基础设施层 │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌────────┐ │
│ │ 注册中心 │ │ 配置中心 │ │ 消息队列 │ │ 链路 │ │
│ │ (Nacos) │ │ (Nacos) │ │(RabbitMQ)│ │追踪 │ │
│ └──────────┘ └──────────┘ └──────────┘ └────────┘ │
└────────────────────────────────────────────────────────┘
五、服务注册与发现(Nacos)
5.1 为什么需要注册中心?
微服务部署后,服务 A 怎么知道服务 B 的 IP 和端口?
- 硬编码 IP?—— 服务重启 IP 就变了
- 配置文件写死?—— 集群部署有多个实例,写哪个?
服务启动 → 向 Nacos 注册自己(IP + 端口 + 服务名)
服务调用 → 从 Nacos 查询目标服务的地址列表
Nacos → 持续心跳检测,自动剔除宕机实例
5.2 Nacos 快速集成
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
spring:
application:
name: order-service
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
namespace: dev
group: DEFAULT_GROUP
@SpringBootApplication
@EnableDiscoveryClient
public class OrderServiceApplication {
public static void main(String[] args) {
SpringApplication.run(OrderServiceApplication.class, args);
}
}
5.3 Nacos 核心机制
┌─────────────────────────────────────────────┐
│ Nacos Server │
│ │
│ 服务注册表: │
│ ┌─────────────┬──────────────────────────┐ │
│ │ 服务名 │ 实例列表 │ │
│ ├─────────────┼──────────────────────────┤ │
│ │ order-svc │ 192.168.1.1:8080 ✅ │ │
│ │ │ 192.168.1.2:8080 ✅ │ │
│ │ user-svc │ 192.168.1.3:8090 ✅ │ │
│ └─────────────┴──────────────────────────┘ │
│ │
│ 心跳机制:每 5 秒上报,15 秒未收到则标记不健康│
└─────────────────────────────────────────────┘
提示:Nacos vs Eureka:Nacos 支持 AP + CP 两种模式切换,且自带配置中心功能,是目前国内主流选择。
六、远程调用(OpenFeign)
6.1 服务间如何调用?
String url = "http://user-service/api/user/" + userId;
User user = restTemplate.getForObject(url, User.class);
User user = userClient.getUserById(userId);
6.2 OpenFeign 完整示例
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
@SpringBootApplication
@EnableFeignClients
public class OrderServiceApplication {
}
@FeignClient(
name = "user-service", // 对应 Nacos 中注册的服务名
path = "/api/user", // 服务的基础路径
fallback = UserClientFallback.class // 降级类
)
public interface UserClient {
@GetMapping("/{id}")
User getUserById(@PathVariable Long id);
@PostMapping("/batch")
List<User> batchGetUsers(@RequestBody List<Long> ids);
}
@Service
@RequiredArgsConstructor
public class OrderService {
private final UserClient userClient;
public OrderVO getOrderDetail(Long orderId) {
Order order = orderMapper.selectById(orderId);
User user = userClient.getUserById(order.getUserId());
return OrderVO.build(order, user);
}
}
6.3 Feign 性能调优
feign:
client:
config:
default:
connect-timeout: 2000
read-timeout: 5000
httpclient:
enabled: false
okhttp:
enabled: true
提示:生产建议:务必替换为 OkHttp 或 HttpClient,默认的 URLConnection 性能差且不支持连接池。
七、负载均衡(LoadBalancer)
7.1 负载均衡原理
当用户服务有 3 个实例时,Feign 调用时如何选择?这就是客户端负载均衡的职责。
订单服务发起调用
▼
Spring Cloud LoadBalancer
├── 从 Nacos 获取 user-service 实例列表
│ [192.168.1.1:8090, 192.168.1.2:8090, 192.168.1.3:8090]
├── 按照负载策略选择一个实例(默认轮询)
└── 发起实际请求
7.2 自定义负载均衡策略
@Bean
public ReactorLoadBalancer<ServiceInstance> customLoadBalancer(
Environment environment,
LoadBalancerClientFactory loadBalancerClientFactory) {
String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
return new RoundRobinLoadBalancer(
loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class),
name
);
}
7.3 常见负载策略对比
| 策略 | 说明 | 适用场景 |
|---|
| 轮询(Round Robin) | 依次轮流,默认策略 | 服务器配置相同 |
| 随机(Random) | 随机选择 | 简单均衡 |
| 加权轮询 | 根据权重分配流量 | 服务器配置不同 |
| 最少连接 | 选择当前连接数最少的 | 请求处理时间差异大 |
八、API 网关(Gateway)
8.1 为什么需要网关?
- 客户端需要知道所有服务地址 → 耦合严重
- 每个服务都要做鉴权 → 重复代码
- 跨域处理分散在各服务 → 难以维护
┌─────────┐
│ 客户端 │
└────┬────┘
│ 所有请求统一走网关
▼
┌──────────────────────────────────┐
│ API Gateway │
│ ✅ 路由转发 ✅ JWT 鉴权 │
│ ✅ 限流熔断 ✅ 日志记录 │
│ ✅ 跨域处理 ✅ 灰度发布 │
└──────┬──────┬──────┬─────────────┘
│ │ │
用户服务 订单服务 商品服务
8.2 Gateway 路由配置
spring:
cloud:
gateway:
routes:
- id: order-service
uri: lb://order-service
predicates:
- Path=/api/order/**
filters:
- StripPrefix=1
- id: user-service
uri: lb://user-service
predicates:
- Path=/api/user/**
- Header=X-Request-Source, app
filters:
- AddRequestHeader=X-From-Gateway,true
8.3 自定义全局过滤器(JWT 鉴权)
@Component
@Order(-1)
@RequiredArgsConstructor
public class AuthGlobalFilter implements GlobalFilter {
private final JwtUtil jwtUtil;
private static final Set<String> WHITE_LIST = Set.of("/api/user/login", "/api/user/register");
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String path = exchange.getRequest().getPath().value();
if (WHITE_LIST.contains(path)) {
return chain.filter(exchange);
}
String token = exchange.getRequest().getHeaders().getFirst("Authorization");
if (token == null || !token.startsWith("Bearer ")) {
return unauthorized(exchange);
}
try {
Claims claims = jwtUtil.parseToken(token.substring(7));
ServerHttpRequest request = exchange.getRequest().mutate()
.header("X-User-Id", claims.getSubject())
.header("X-User-Role", claims.get("role", String.class))
.build();
return chain.filter(exchange.mutate().request(request).build());
} catch (JwtException e) {
return unauthorized(exchange);
}
}
private Mono<Void> unauthorized(ServerWebExchange exchange) {
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
}
8.4 网关限流配置
spring:
cloud:
gateway:
routes:
- id: order-service
uri: lb://order-service
predicates:
- Path=/api/order/**
filters:
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 100
redis-rate-limiter.burstCapacity: 200
key-resolver: "#{@ipKeyResolver}"
@Bean
public KeyResolver ipKeyResolver() {
return exchange -> Mono.just(Objects.requireNonNull(exchange.getRequest().getRemoteAddress()).getAddress().getHostAddress());
}
九、配置中心(Nacos Config)
9.1 配置中心解决什么问题?
- 修改数据库密码 → 需要改几十个配置文件并重新部署?❌
- 不同环境(dev/test/prod)配置不同 → 怎么管理?
9.2 集成 Nacos Config
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
bootstrap.yaml(优先级高于 application.yaml):
spring:
application:
name: order-service
cloud:
nacos:
config:
server-addr: 127.0.0.1:8848
file-extension: yaml
namespace: dev
9.3 动态刷新配置
@RestController
@RefreshScope
public class OrderController {
@Value("${order.timeout:30}")
private Integer orderTimeout;
@Value("${order.max-quantity:99}")
private Integer maxQuantity;
@GetMapping("/config")
public String getConfig() {
return "超时:" + orderTimeout + "s, 最大数量:" + maxQuantity;
}
}
提示:在 Nacos 控制台修改配置后,无需重启服务,@RefreshScope 注解的 Bean 会自动更新!
十、熔断与限流(Sentinel)
10.1 为什么需要熔断?
- C 等待 D → C 的线程被耗尽
- B 等待 C → B 的线程被耗尽
- A 等待 B → A 崩溃
- 雪崩效应!整个链路崩溃
熔断器(Circuit Breaker)就是保险丝:当下游服务故障时,自动切断调用,快速失败,保护上游。
状态机:
┌─────────┐ 失败率超阈值 ┌─────────┐
│ CLOSED │─────────────▶│ OPEN │
│(正常) │ │(熔断) │
└─────────┘ └────┬────┘
▲ │
│ │ 等待恢复窗口
│ ▼
┌───────────────────────┐ │
│ HALF_OPEN │◀───┘
│(半开,放少量请求探测)│
└───────────────────────┘
10.2 Sentinel 集成
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
spring:
cloud:
sentinel:
transport:
dashboard: localhost:8080
eager: true
feign:
sentinel:
enabled: true
10.3 FeignClient 降级实现
@Component
public class UserClientFallback implements UserClient {
@Override
public User getUserById(Long id) {
return User.builder().id(id).username("未知用户").build();
}
@Override
public List<User> batchGetUsers(List<Long> ids) {
return Collections.emptyList();
}
}
@FeignClient(name = "user-service", fallback = UserClientFallback.class)
public interface UserClient { ... }
10.4 @SentinelResource 注解
@Service
public class OrderService {
@SentinelResource(
value = "createOrder", // 资源名
blockHandler = "createOrderBlockHandler", // 限流/熔断时的处理方法
fallback = "createOrderFallback" // 异常时的降级方法
)
public OrderVO createOrder(CreateOrderDTO dto) {
return doCreateOrder(dto);
}
public OrderVO createOrderBlockHandler(CreateOrderDTO dto, BlockException ex) {
throw new BusinessException("系统繁忙,请稍后重试");
}
public OrderVO createOrderFallback(CreateOrderDTO dto, Throwable t) {
log.error("创建订单失败,进入降级", t);
return OrderVO.failed("服务暂时不可用");
}
}
十一、分布式事务(Seata)
11.1 分布式事务的难题
用户下单
├── 订单服务:创建订单
├── 库存服务:扣减库存
└── 账户服务:扣减余额
如果库存扣减成功,但账户扣减失败 → 数据不一致!
11.2 Seata AT 模式原理
Seata AT 模式通过自动补偿实现分布式事务:
┌──────────────────────────────────────────────────┐
│ 第一阶段(执行) │
│ 1. 解析 SQL,生成前镜像(before image) │
│ 2. 执行业务 SQL │
│ 3. 生成后镜像(after image),写入 undo_log 表 │
│ 4. 向 TC(事务协调者)注册分支事务 │
└──────────────────────────────────────────────────┘
✅ 所有分支成功 → 第二阶段提交(清理 undo_log)
❌ 任意分支失败 → 第二阶段回滚(根据 undo_log 还原数据)
11.3 Seata 使用示例
@Service
@RequiredArgsConstructor
public class OrderService {
private final StockClient stockClient;
private final AccountClient accountClient;
private final OrderMapper orderMapper;
@GlobalTransactional(name = "create-order", rollbackFor = Exception.class)
public void createOrder(CreateOrderDTO dto) {
Order order = Order.from(dto);
orderMapper.insert(order);
stockClient.deduct(dto.getProductId(), dto.getQuantity());
accountClient.deduct(dto.getUserId(), dto.getTotalAmount());
}
}
11.4 四种分布式事务方案对比
| 方案 | 一致性 | 性能 | 复杂度 | 适用场景 |
|---|
| Seata AT | 强一致 | 中 | 低(侵入少) | 大多数场景 |
| Seata TCC | 强一致 | 高 | 高(手写补偿) | 高并发、高性能 |
| 本地消息表 | 最终一致 | 高 | 中 | 允许延迟一致 |
| Saga | 最终一致 | 高 | 高 | 长事务、跨组织 |
十二、链路追踪(SkyWalking)
12.1 为什么需要链路追踪?
一个请求经过:Gateway → 订单服务 → 用户服务 → 商品服务
某次请求响应慢,是哪个环节慢?没有链路追踪完全抓瞎。
- 完整调用链可视化
- 每个服务的耗时分析
- 慢请求、错误请求告警
12.2 核心概念
Trace(一次完整请求)
└── Span(一个操作单元)
├── Span: Gateway 路由 [0ms - 5ms]
├── Span: OrderService.createOrder [5ms - 150ms]
│ ├── Span: DB query orders [5ms - 20ms]
│ ├── Span: Feign -> user-service [20ms - 80ms]
│ └── Span: Feign -> stock-service [80ms - 145ms]
└── Span: 返回响应 [150ms - 152ms]
12.3 接入方式
SkyWalking 采用 Java Agent 方式,完全无代码侵入:
java -javaagent:/path/to/skywalking-agent.jar \
-Dskywalking.agent.service_name=order-service \
-Dskywalking.collector.backend_service=127.0.0.1:11800 \
-jar order-service.jar
十三、消息驱动(RabbitMQ / Kafka)
13.1 消息队列在微服务中的价值
场景:用户下单后,需要:发短信通知、更新积分、推送优惠券……
下单 → 发短信 → 更新积分 → 推送优惠券
每步都要等待,总耗时 = 各步之和,且任一失败影响下单
下单 → 发送消息 → 立即返回(异步)
├── 短信服务(消费消息)
├── 积分服务(消费消息)
└── 优惠券服务(消费消息)
13.2 Spring AMQP 实战(RabbitMQ)
@Service
@RequiredArgsConstructor
public class OrderService {
private final RabbitTemplate rabbitTemplate;
@Transactional
public void createOrder(CreateOrderDTO dto) {
Order order = doCreateOrder(dto);
OrderCreatedEvent event = OrderCreatedEvent.from(order);
rabbitTemplate.convertAndSend("order.exchange",
"order.created",
event
);
log.info("订单创建成功,事件已发送:orderId={}", order.getId());
}
}
@Component
@Slf4j
public class SmsConsumer {
@RabbitListener(bindings = @QueueBinding(
value = @Queue(name = "sms.order.created", durable = "true"),
exchange = @Exchange(name = "order.exchange", type = "topic"),
key = "order.created"
))
public void handleOrderCreated(OrderCreatedEvent event) {
log.info("收到订单创建事件,发送短信:userId={}", event.getUserId());
try {
smsService.sendOrderConfirmation(event.getUserId(), event.getOrderNo());
} catch (Exception e) {
log.error("短信发送失败", e);
throw e;
}
}
}
13.3 消息可靠性保障
生产者可靠性:
├── Publisher Confirms:消息到达 Exchange 后 ack
└── Publisher Returns:消息无法路由时回调
Broker 可靠性:
├── 队列持久化:durable = true
└── 消息持久化:deliveryMode = 2
消费者可靠性:
├── 手动 ACK:消费成功后才确认
├── 重试机制:失败后延迟重试(指数退避)
└── 死信队列:超过重试次数后转入 DLQ,人工介入
十四、微服务最佳实践与避坑指南
14.1 服务拆分原则
✅ 正确姿势:
- 按业务领域拆分(DDD 限界上下文)
- 每个服务可由 2-3 人独立维护
- 高内聚、低耦合
❌ 错误姿势:
- 按技术层次拆分(Controller 层、Service 层各一个服务)
- 拆分粒度过细(每个方法一个服务)
- 两个服务之间循环依赖
14.2 接口版本管理
@RestController
@RequestMapping("/api/v1/order")
public class OrderControllerV1 { ... }
@RestController
@RequestMapping("/api/v2/order")
public class OrderControllerV2 { ... }
14.3 超时设计规范
gateway:
timeout: 10s
feign:
read-timeout: 8s
service:
business-timeout: 5s
14.4 生产必备 Checklist
部署前检查:
✅ 每个服务配置了健康检查接口 /actuator/health
✅ 数据库连接池大小合理(不要用默认值)
✅ 日志级别配置正确(生产不要 DEBUG)
✅ 所有 Feign 调用配置了超时和降级
✅ 敏感配置通过配置中心管理,不 hardcode
✅ 接口幂等性处理(防止重复消费/重复提交)
✅ 分布式锁防止并发问题
监控告警:
✅ 接入链路追踪(SkyWalking)
✅ 关键指标监控(Prometheus + Grafana)
✅ 错误日志告警(ELK + 告警规则)
✅ 服务可用性告警(低于 99.9% 立即通知)
14.5 常见踩坑记录
feign:
client:
config:
default:
connect-timeout: 2000
read-timeout: 5000
spring:
cloud:
nacos:
discovery:
heart-beat-interval: 5000
heart-beat-timeout: 15000
ip-delete-timeout: 30000
坑 3:@GlobalTransactional 和本地事务冲突
@GlobalTransactional
@Transactional
public void createOrder() { ... }
@GlobalTransactional(rollbackFor = Exception.class)
public void createOrder() { ... }
十五、总结:微服务架构全貌
┌─────────────────────────────────────────────────────┐
│ 微服务技术栈全景 │
└─────────────────────────────────────────────────────┘
流量入口层 [客户端] ──► [API Gateway(路由/鉴权/限流)]
服务治理层 [Nacos 注册中心] + [Nacos 配置中心]
业务服务层 [用户服务] [订单服务] [商品服务] [支付服务] ...
通信层 [OpenFeign 同步调用] + [RabbitMQ/Kafka 异步消息]
稳定性保障 [Sentinel 熔断限流] + [Seata 分布式事务]
可观测性 [SkyWalking 链路追踪] + [Prometheus 监控] + [ELK 日志]
容器化部署 [Docker] + [Kubernetes] + [Helm]
学习路径建议
阶段一(入门): Spring Boot → Nacos 注册发现 → OpenFeign 调用
阶段二(基础): Gateway 网关 → Nacos 配置中心 → Sentinel 熔断
阶段三(进阶): Seata 分布式事务 → MQ 异步解耦 → SkyWalking 追踪
阶段四(深入): DDD 领域驱动设计 → 服务网格(Istio) → K8s 编排
写在最后
微服务不是目的,解决业务问题才是。理解每个组件解决的核心痛点,比记住配置更重要。
建议从搭建一个完整的 demo 项目开始,把所有组件串联起来,在实战中加深理解。
相关免费在线工具
- Keycode 信息
查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
- Escape 与 Native 编解码
JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
- JavaScript / HTML 格式化
使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
- JavaScript 压缩与混淆
Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
- Base64 字符串编码/解码
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
- Base64 文件转换器
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online