微服务架构:从入门到进阶完全指南

🚀 微服务架构:从入门到进阶完全指南

一、为什么需要微服务?

1.1 从一个真实痛点说起

想象一个电商系统,所有功能都写在一个项目里:用户模块、订单模块、商品模块、支付模块......

随着业务增长,问题开始浮现:

  • 🔴 部署风险极高:改一个支付 Bug,整个系统都要重新部署
  • 🔴 扩展困难:订单模块压力大,但没法单独扩容,只能整体扩
  • 🔴 技术栈被锁死:想在某个模块用 Go 提升性能?不可能
  • 🔴 团队协作灾难:100人同时改同一个项目,合并代码就是噩梦
  • 🔴 启动慢如蜗牛:本地跑个项目要等 3 分钟

这就是单体架构的天花板,而微服务正是为了打破这个天花板而生。


二、什么是微服务?

2.1 官方定义

微服务 (Microservices)是一种将单一应用程序拆分为一组小型、独立、可独立部署的服务的架构风格。每个服务运行在自己的进程中,通过轻量级通信机制(通常是 HTTP/REST 或消息队列)进行交互。

2.2 用人话解释

把大象切成小块,每块独立运作:

复制代码
电商系统(单体)              电商系统(微服务)
┌─────────────────────┐      ┌──────────┐  ┌──────────┐
│                     │      │ 用户服务  │  │ 订单服务  │
│  用户 + 订单 + 商品  │  →   └──────────┘  └──────────┘
│  + 支付 + 物流 + ... │      ┌──────────┐  ┌──────────┐
│                     │      │ 商品服务  │  │ 支付服务  │
└─────────────────────┘      └──────────┘  └──────────┘

每个服务:

  • 独立的代码仓库
  • 独立的数据库
  • 可以独立部署、独立扩容
  • 可以用不同的技术栈开发

2.3 微服务的核心原则

原则 说明
单一职责 每个服务只做一件事,做好一件事
自治性 服务间松耦合,独立开发、独立部署
去中心化 数据管理去中心化,每服务拥有自己的数据
容错设计 任何服务都可能失败,系统必须能优雅降级
可观测性 日志、指标、链路追踪缺一不可

三、单体 vs 微服务对比

维度 单体架构 微服务架构
开发效率 初期快,后期慢 初期慢,后期快
部署 整体部署,风险高 独立部署,风险低
扩展性 整体水平扩展 按需精准扩展
技术栈 统一技术栈 自由选择
故障影响 一点崩溃,全体崩溃 故障隔离,影响范围小
运维复杂度 高(需要完善的基础设施)
适用场景 初创、业务简单 大型、复杂、高并发业务

⚠️ 重要认知:微服务不是银弹!小团队、简单业务用微服务是过度设计,会带来巨大的运维负担。


四、微服务核心组件全景图

复制代码
                        ┌─────────────────────────────────────────────┐
                        │                  客户端                      │
                        └─────────────────────┬───────────────────────┘
                                              │
                        ┌─────────────────────▼───────────────────────┐
                        │            API 网关 (Gateway)                │
                        │    路由 / 鉴权 / 限流 / 熔断 / 灰度发布       │
                        └──────┬──────────────┬──────────────┬────────┘
                               │              │              │
               ┌───────────────▼──┐  ┌────────▼─────┐  ┌───▼───────────┐
               │    用户服务       │  │   订单服务    │  │   商品服务    │
               │  (user-service)  │  │(order-service)│  │(goods-service)│
               └───────┬──────────┘  └──────┬────────┘  └───┬───────────┘
                       │                    │               │
               ┌───────▼────────────────────▼───────────────▼───────────┐
               │                   基础设施层                            │
               │  ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌────────┐ │
               │  │ 注册中心  │  │ 配置中心  │  │ 消息队列  │  │ 链路  │ │
               │  │ (Nacos)  │  │ (Nacos)  │  │(RabbitMQ)│  │追踪   │ │
               │  └──────────┘  └──────────┘  └──────────┘  └────────┘ │
               └────────────────────────────────────────────────────────┘

五、服务注册与发现(Nacos)

5.1 为什么需要注册中心?

微服务部署后,服务 A 怎么知道服务 B 的 IP 和端口?

  • 硬编码 IP?------ 服务重启 IP 就变了
  • 配置文件写死?------ 集群部署有多个实例,写哪个?

注册中心就是微服务世界的"通讯录":

复制代码
服务启动 → 向 Nacos 注册自己(IP + 端口 + 服务名)
服务调用 → 从 Nacos 查询目标服务的地址列表
Nacos   → 持续心跳检测,自动剔除宕机实例

5.2 Nacos 快速集成

引入依赖:

xml 复制代码
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

配置文件:

yaml 复制代码
spring:
  application:
    name: order-service        # 服务名,注册到 Nacos 的唯一标识
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848   # Nacos 地址
        namespace: dev                 # 命名空间(环境隔离)
        group: DEFAULT_GROUP

启动类开启注册:

java 复制代码
@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 服务间如何调用?

订单服务需要查询用户信息,怎么调用用户服务?

原始方式(RestTemplate):

java 复制代码
// 繁琐,需要手动拼接 URL,不优雅
String url = "http://user-service/api/user/" + userId;
User user = restTemplate.getForObject(url, User.class);

OpenFeign 方式(声明式,优雅):

java 复制代码
// 像调用本地方法一样调用远程服务 ✨
User user = userClient.getUserById(userId);

6.2 OpenFeign 完整示例

依赖:

xml 复制代码
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

启动类开启 Feign:

java 复制代码
@SpringBootApplication
@EnableFeignClients  // 扫描所有 FeignClient
public class OrderServiceApplication { ... }

定义 FeignClient 接口:

java 复制代码
@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);
}

使用:

java 复制代码
@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 性能调优

yaml 复制代码
feign:
  client:
    config:
      default:
        connect-timeout: 2000    # 连接超时 2s
        read-timeout: 5000       # 读取超时 5s
  httpclient:
    enabled: false
  okhttp:
    enabled: true               # 使用 OkHttp 替换默认 URLConnection,支持连接池

💡 生产建议:务必替换为 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 自定义负载均衡策略

java 复制代码
/**
 * 自定义:优先调用同机房实例(降低跨机房延迟)
 */
@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 路由配置

yaml 复制代码
spring:
  cloud:
    gateway:
      routes:
        # 订单服务路由
        - id: order-service
          uri: lb://order-service     # lb:// 表示从注册中心负载均衡
          predicates:
            - Path=/api/order/**      # 匹配路径
          filters:
            - StripPrefix=1           # 去掉前缀 /api

        # 用户服务路由
        - id: user-service
          uri: lb://user-service
          predicates:
            - Path=/api/user/**
            - Header=X-Request-Source, app  # 只接受 app 来源
          filters:
            - AddRequestHeader=X-From-Gateway, true

8.3 自定义全局过滤器(JWT 鉴权)

java 复制代码
/**
 * 全局 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);
        }

        // 获取并校验 Token
        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 网关限流配置

yaml 复制代码
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}"       # 按 IP 限流
java 复制代码
@Bean
public KeyResolver ipKeyResolver() {
    // 按客户端 IP 进行限流
    return exchange -> Mono.just(
        Objects.requireNonNull(exchange.getRequest().getRemoteAddress())
               .getAddress().getHostAddress()
    );
}

九、配置中心(Nacos Config)

9.1 配置中心解决什么问题?

微服务有几十个服务,每个服务都有配置文件:

  • 修改数据库密码 → 需要改几十个配置文件并重新部署?❌
  • 不同环境(dev/test/prod)配置不同 → 怎么管理?

配置中心让配置集中管理、动态刷新,无需重启服务。

9.2 集成 Nacos Config

依赖:

xml 复制代码
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>

bootstrap.yaml(优先级高于 application.yaml):

yaml 复制代码
spring:
  application:
    name: order-service
  cloud:
    nacos:
      config:
        server-addr: 127.0.0.1:8848
        file-extension: yaml         # 配置文件格式
        namespace: dev
        # 读取的配置文件:${spring.application.name}-${spring.profiles.active}.${file-extension}
        # 即:order-service-dev.yaml

9.3 动态刷新配置

java 复制代码
@RestController
@RefreshScope  // 关键注解:配置变更时自动刷新 Bean
public class OrderController {

    @Value("${order.timeout:30}")       // 从 Nacos 读取,默认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 为什么需要熔断?

微服务调用链:A → B → C → D

如果 D 服务响应慢,会导致:

  • C 等待 D → C 的线程被耗尽
  • B 等待 C → B 的线程被耗尽
  • A 等待 B → A 崩溃
  • 雪崩效应!整个链路崩溃

熔断器(Circuit Breaker)就是保险丝:当下游服务故障时,自动切断调用,快速失败,保护上游。

复制代码
状态机:
┌─────────┐  失败率超阈值   ┌─────────┐
│ CLOSED  │─────────────▶│  OPEN   │
│(正常)  │              │(熔断)  │
└─────────┘              └────┬────┘
     ▲                        │ 等待恢复窗口
     │         成功             ▼
     │        ┌───────────────────────┐
     └────────│    HALF_OPEN          │
              │(半开,放少量请求探测)│
              └───────────────────────┘

10.2 Sentinel 集成

依赖:

xml 复制代码
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>

配置:

yaml 复制代码
spring:
  cloud:
    sentinel:
      transport:
        dashboard: localhost:8080   # Sentinel 控制台地址
      eager: true                   # 立即初始化
feign:
  sentinel:
    enabled: true                   # 开启 Feign 的 Sentinel 支持

10.3 FeignClient 降级实现

java 复制代码
/**
 * UserClient 的降级实现
 * 当 user-service 不可用时,返回兜底数据
 */
@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 指定 fallback
@FeignClient(name = "user-service", fallback = UserClientFallback.class)
public interface UserClient { ... }

10.4 @SentinelResource 注解

java 复制代码
@Service
public class OrderService {

    @SentinelResource(
        value = "createOrder",                    // 资源名
        blockHandler = "createOrderBlockHandler", // 限流/熔断时的处理方法
        fallback = "createOrderFallback"          // 异常时的降级方法
    )
    public OrderVO createOrder(CreateOrderDTO dto) {
        // 正常业务逻辑
        return doCreateOrder(dto);
    }

    // 限流处理:必须是 public,参数列表加 BlockException
    public OrderVO createOrderBlockHandler(CreateOrderDTO dto, BlockException ex) {
        throw new BusinessException("系统繁忙,请稍后重试");
    }

    // 降级处理:必须是 public,参数列表加 Throwable
    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 使用示例

java 复制代码
/**
 * 全局事务:@GlobalTransactional 注解
 * 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) {
        // 1. 创建订单(本地事务)
        Order order = Order.from(dto);
        orderMapper.insert(order);

        // 2. 扣减库存(远程调用,Seata 自动管理)
        stockClient.deduct(dto.getProductId(), dto.getQuantity());

        // 3. 扣减账户余额(远程调用,Seata 自动管理)
        accountClient.deduct(dto.getUserId(), dto.getTotalAmount());

        // 任意步骤抛出异常,Seata 自动回滚所有已执行的操作
    }
}

11.4 四种分布式事务方案对比

方案 一致性 性能 复杂度 适用场景
Seata AT 强一致 低(侵入少) 大多数场景
Seata TCC 强一致 高(手写补偿) 高并发、高性能
本地消息表 最终一致 允许延迟一致
Saga 最终一致 长事务、跨组织

十二、链路追踪(SkyWalking)

12.1 为什么需要链路追踪?

一个请求经过:Gateway → 订单服务 → 用户服务 → 商品服务

某次请求响应慢,是哪个环节慢?没有链路追踪完全抓瞎。

SkyWalking 提供:

  • 完整调用链可视化
  • 每个服务的耗时分析
  • 慢请求、错误请求告警

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 方式,完全无代码侵入:

bash 复制代码
# 启动服务时添加 javaagent 参数
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)

生产者(订单服务):

java 复制代码
@Service
@RequiredArgsConstructor
public class OrderService {

    private final RabbitTemplate rabbitTemplate;

    @Transactional
    public void createOrder(CreateOrderDTO dto) {
        // 1. 创建订单
        Order order = doCreateOrder(dto);

        // 2. 发送消息(异步通知其他服务)
        OrderCreatedEvent event = OrderCreatedEvent.from(order);
        rabbitTemplate.convertAndSend(
            "order.exchange",           // Exchange 名
            "order.created",            // Routing Key
            event                       // 消息体(会被序列化为 JSON)
        );

        log.info("订单创建成功,事件已发送: orderId={}", order.getId());
    }
}

消费者(短信服务):

java 复制代码
@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 接口版本管理

java 复制代码
// 使用 URL 版本号,避免接口升级导致调用方崩溃
@RestController
@RequestMapping("/api/v1/order")
public class OrderControllerV1 { ... }

@RestController
@RequestMapping("/api/v2/order")
public class OrderControllerV2 { ... }

14.3 超时设计规范

yaml 复制代码
# 超时链:网关超时 > Feign 超时 > 业务超时
# 保证超时能从最底层传播到最上层

gateway:
  timeout: 10s        # 网关最长等待 10s

feign:
  read-timeout: 8s    # Feign 调用超时 8s(小于网关)

service:
  business-timeout: 5s  # 业务操作超时 5s(小于 Feign)

14.4 生产必备 Checklist

复制代码
部署前检查:
  ✅ 每个服务配置了健康检查接口 /actuator/health
  ✅ 数据库连接池大小合理(不要用默认值)
  ✅ 日志级别配置正确(生产不要 DEBUG)
  ✅ 所有 Feign 调用配置了超时和降级
  ✅ 敏感配置通过配置中心管理,不 hardcode
  ✅ 接口幂等性处理(防止重复消费/重复提交)
  ✅ 分布式锁防止并发问题

监控告警:
  ✅ 接入链路追踪(SkyWalking)
  ✅ 关键指标监控(Prometheus + Grafana)
  ✅ 错误日志告警(ELK + 告警规则)
  ✅ 服务可用性告警(低于 99.9% 立即通知)

14.5 常见踩坑记录

坑1:Feign 不配置超时导致线程池耗尽

yaml 复制代码
# 一定要配!默认超时是无限等待!
feign:
  client:
    config:
      default:
        connect-timeout: 2000
        read-timeout: 5000

坑2:Nacos 实例心跳失效被剔除

yaml 复制代码
# 延长心跳间隔,防止 GC 暂停导致误判下线
spring:
  cloud:
    nacos:
      discovery:
        heart-beat-interval: 5000      # 心跳间隔 5s
        heart-beat-timeout: 15000      # 超时时间 15s
        ip-delete-timeout: 30000       # 删除超时 30s

坑3:@GlobalTransactional 和本地事务冲突

java 复制代码
// ❌ 错误:不要在 @GlobalTransactional 方法上同时用 @Transactional
@GlobalTransactional
@Transactional  // 会产生嵌套事务问题
public void createOrder() { ... }

// ✅ 正确:只用 @GlobalTransactional,Seata 会自动管理本地事务
@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 项目开始,把所有组件串联起来,在实战中加深理解。


如有问题或建议,欢迎在评论区交流!🚀

相关推荐
2501_926978331 小时前
思想波与引力共振理论:统一物理主义意识框架的革命性探索--AGI理论系统基础12
人工智能·经验分享·架构·langchain·agi
Javatutouhouduan1 小时前
RocketMQ是怎么保存偏移量的?
java·消息队列·rocketmq·java面试·消息中间件·后端开发·java程序员
天若有情6731 小时前
IoC不止Spring!求同vs存异,两种反向IoC的核心逻辑
java·c++·后端·算法·spring·架构·ioc
绝无仅有2 小时前
mac笔记本中在PHP中调用Java JAR包的指南
后端·面试·架构
绝无仅有2 小时前
PHP与Java项目在服务器上的对接准备与过程
后端·面试·架构
彭于晏Yan2 小时前
LangChain4j实战三:图像模型
java·spring boot·后端·langchain
SimonKing2 小时前
跨越数据孤岛!SpringBoot使用JDBC调用Calcite联邦查询实战
java·后端·程序员
好家伙VCC2 小时前
# 发散创新:基于Python的TTS语音合成实战与优化策略 在人工智能加速落地的今天,**文本转
java·开发语言·人工智能·python
等D春C夏X3 小时前
最终版C++11/14/17学习大纲(精准核对42条条款)
java·开发语言