微服务Java分类指南

一、OpenFeign 远程调用

1.1 核心注解 @FeignClient

@FeignClient 是 Spring Cloud OpenFeign 的核心注解,用于声明远程 HTTP 调用客户端。

核心属性详解

属性 作用 默认值
value/name指定目标微服务名称""
contextIdFeign 上下文唯一标识,解决同服务多客户端冲突""
url硬编码目标地址,不走服务发现""
path统一接口前缀路径""
configuration专属配置类(日志、超时等){}
fallback服务降级类void.class
fallbackFactory降级工厂(可捕获异常)void.class

常用使用场景

场景1:标准服务发现模式

@FeignClient(name = "user-service", contextId = "userFeign", path = "/api/user")
public interface UserFeign {
    @GetMapping("/get")
    UserDTO getUser(@RequestParam Long id);
}

场景2:本地调试(直连地址)

@FeignClient(url = "http://127.0.0.1:8081", name = "debug-user")
public interface UserFeign {}

场景3:带降级工厂的完整配置

@FeignClient(
    name = "order-service",
    contextId = "orderFeign",
    configuration = FeignConfig.class,
    fallbackFactory = OrderFallbackFactory.class
)
public interface OrderFeign {}

1.2 降级策略对比

类型 特点 适用场景
fallback仅返回默认值,无法获取异常信息简单降级场景
fallbackFactory可捕获 Throwable,根据异常类型处理需要区分异常类型的复杂场景

fallbackFactory 示例

@Component
public class UserFallbackFactory implements FallbackFactory<UserFeign> {
    @Override
    public UserFeign create(Throwable throwable) {
        return id -> {
            log.error("调用用户服务异常:", throwable);
            return new UserDTO(-1L, "服务繁忙,请稍后重试");
        };
    }
}

二、Spring Boot 自动配置机制

2.1 自动配置执行流程

@SpringBootApplication
        ↓ 解析
@EnableAutoConfiguration
        ↓
AutoConfigurationImportSelector
        ↓ 读取
AutoConfiguration.imports 或 spring.factories
        ↓ 过滤(按条件)
符合 @Conditional 的配置类 → 注册 Bean

2.2 关键注解解析

@SpringBootApplication 组合注解

  • @SpringBootConfiguration → 等价于 @Configuration
  • @EnableAutoConfiguration → 开启自动配置的核心入口
  • @ComponentScan → 包扫描(仅扫描启动类所在包)

2.3 配置文件位置(Spring Boot 3.x)

resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

2.4 条件注解过滤机制

  • @ConditionalOnClass(Redis.class) → 仅当存在 Redis 依赖时加载
  • @ConditionalOnMissingBean(RedisTemplate.class) → 仅当用户未自定义时加载
  • @ConditionalOnProperty → 根据配置属性值判断

三、网关 Gateway

3.1 依赖架构

ruoyi-gateway 依赖架构
│
├─────────────────────────────────────────────────────────────┤
│ 核心网关层                                                   │
│ ├─ Spring Cloud Gateway (webflux)                           │
│ └─ Spring Cloud Loadbalancer (负载均衡)                     │
├─────────────────────────────────────────────────────────────┤
│ 服务治理层                                                   │
│ ├─ Nacos Discovery (服务发现)                               │
│ └─ Nacos Config (配置中心)                                  │
├─────────────────────────────────────────────────────────────┤
│ 流量治理层                                                   │
│ ├─ Sentinel Core (限流熔断核心)                              │
│ ├─ Sentinel Gateway (网关限流)                              │
│ └─ Sentinel Datasource Nacos (规则持久化)                    │
├─────────────────────────────────────────────────────────────┤
│ 辅助功能层                                                   │
│ ├─ Spring Boot Actuator (健康检查)                          │
│ ├─ Kaptcha (验证码)                                         │
│ ├─ RuoYi Redis (缓存支持)                                   │
│ └─ Springdoc (API文档)                                      │
└─────────────────────────────────────────────────────────────

3.2 路由配置示例

spring:
  cloud:
    gateway:
      routes:
        - id: auth_route
          uri: lb://ruoyi-auth  # lb:// 协议表示使用负载均衡
          predicates:
            - Path=/auth/**
          filters:
            - StripPrefix=1

3.3 负载均衡配置

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-loadbalancer</artifactId>
</dependency>

四、Kafka 消息队列

4.1 Maven 集成

pom.xml 中添加 Spring Kafka 依赖:

<dependency>
    <groupId>org.springframework.kafka</groupId>
    <artifactId>spring-kafka</artifactId>
</dependency>

<!-- 如果需要手动指定版本 -->
<dependency>
    <groupId>org.springframework.kafka</groupId>
    <artifactId>spring-kafka</artifactId>
    <version>3.1.2</version>
</dependency>

4.2 YML 配置

server:
  port: 8080

spring:
  kafka:
    bootstrap-servers: localhost:9092
    
    # 生产者配置
    producer:
      key-serializer: org.apache.kafka.common.serialization.StringSerializer
      value-serializer: org.springframework.kafka.support.serializer.JsonSerializer
      retries: 3
      batch-size: 16384
      buffer-memory: 33554432
      acks: all
    
    # 消费者配置
    consumer:
      key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
      value-deserializer: org.springframework.kafka.support.serializer.JsonDeserializer
      group-id: order-consumer-group
      enable-auto-commit: true
      auto-commit-interval: 1000
      auto-offset-reset: earliest
      properties:
        spring:
          json:
            trusted:
              packages: com.study.kafka.dto

4.3 生产者发送消息

配置类

@Configuration
public class KafkaProducerConfig {
    
    @Value("${spring.kafka.bootstrap-servers}")
    private String bootstrapServers;
    
    @Bean
    public ProducerFactory<String, Object> producerFactory() {
        Map<String, Object> configProps = new HashMap<>();
        configProps.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers);
        configProps.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
        configProps.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, JsonSerializer.class);
        configProps.put(ProducerConfig.ACKS_CONFIG, "all");
        configProps.put(ProducerConfig.RETRIES_CONFIG, 3);
        return new DefaultKafkaProducerFactory<>(configProps);
    }
    
    @Bean
    public KafkaTemplate<String, Object> kafkaTemplate() {
        return new KafkaTemplate<>(producerFactory());
    }
}

生产者服务

@Service
public class KafkaProducerService {
    
    private static final String TOPIC = "order-topic";
    
    @Autowired
    private KafkaTemplate<String, Object> kafkaTemplate;
    
    // 同步发送
    public SendResult<String, Object> sendMessageSync(String key, Object message) {
        try {
            return kafkaTemplate.send(TOPIC, key, message).get();
        } catch (InterruptedException | ExecutionException e) {
            Thread.currentThread().interrupt();
            throw new RuntimeException("发送消息失败", e);
        }
    }
    
    // 异步发送
    public void sendMessageAsync(String key, Object message) {
        kafkaTemplate.send(TOPIC, key, message)
            .addCallback(
                result -> log.info("发送成功:offset={}", result.getRecordMetadata().offset()),
                ex -> log.error("发送失败", ex)
            );
    }
}

4.4 消费者监听消费

配置类

@Configuration
public class KafkaConsumerConfig {
    
    @Value("${spring.kafka.bootstrap-servers}")
    private String bootstrapServers;
    
    @Value("${spring.kafka.consumer.group-id}")
    private String groupId;
    
    @Bean
    public ConsumerFactory<String, Object> consumerFactory() {
        Map<String, Object> configProps = new HashMap<>();
        configProps.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers);
        configProps.put(ConsumerConfig.GROUP_ID_CONFIG, groupId);
        configProps.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
        configProps.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, JsonDeserializer.class);
        configProps.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest");
        configProps.put(JsonDeserializer.TRUSTED_PACKAGES, "com.study.kafka.dto");
        return new DefaultKafkaConsumerFactory<>(configProps);
    }
    
    @Bean
    public ConcurrentKafkaListenerContainerFactory<String, Object> kafkaListenerContainerFactory() {
        ConcurrentKafkaListenerContainerFactory<String, Object> factory = 
            new ConcurrentKafkaListenerContainerFactory<>();
        factory.setConsumerFactory(consumerFactory());
        factory.setConcurrency(3);
        factory.setBatchListener(true);
        return factory;
    }
}

消费者服务

@Service
public class KafkaConsumerService {
    
    private static final String TOPIC = "order-topic";
    
    // 单条消息消费
    @KafkaListener(topics = TOPIC, groupId = "order-consumer-group")
    public void consumeMessage(@Payload OrderDTO order, @Header(KafkaHeaders.RECEIVED_KEY) String key) {
        log.info("收到消息:key={}, order={}", key, order);
        processOrder(order);
    }
    
    // 批量消息消费
    @KafkaListener(topics = TOPIC, groupId = "order-consumer-group", containerFactory = "kafkaListenerContainerFactory")
    public void consumeBatchMessages(@Payload List<OrderDTO> orders) {
        log.info("批量收到 {} 条消息", orders.size());
        for (OrderDTO order : orders) {
            processOrder(order);
        }
    }
    
    // 手动提交偏移量
    @KafkaListener(topics = TOPIC, groupId = "order-consumer-group")
    public void consumeWithManualAck(@Payload OrderDTO order, Acknowledgment acknowledgment) {
        try {
            log.info("收到消息:{}", order);
            processOrder(order);
            acknowledgment.acknowledge();
        } catch (Exception e) {
            log.error("消费失败", e);
            // 不提交偏移量,消息会重新消费
        }
    }
    
    private void processOrder(OrderDTO order) {
        // 业务逻辑处理
    }
}

4.5 保证幂等性

方案一:数据库唯一约束

@Service
public class OrderService {
    
    @Autowired
    private OrderRepository orderRepository;
    
    @Transactional
    public void processOrder(OrderDTO order) {
        if (orderRepository.existsByOrderId(order.getOrderId())) {
            log.warn("订单已处理:{}", order.getOrderId());
            return;
        }
        
        OrderEntity entity = new OrderEntity();
        entity.setOrderId(order.getOrderId());
        entity.setAmount(order.getAmount());
        entity.setStatus("COMPLETED");
        orderRepository.save(entity);
    }
}

方案二:Redis 分布式锁 + 幂等键

@Service
public class IdempotentService {
    
    @Autowired
    private StringRedisTemplate redisTemplate;
    
    private static final String PREFIX = "order:idempotent:";
    private static final long EXPIRATION = 300L;
    
    public boolean checkAndLock(String orderId) {
        String key = PREFIX + orderId;
        Boolean success = redisTemplate.opsForValue()
            .setIfAbsent(key, "processing", EXPIRATION, TimeUnit.SECONDS);
        return Boolean.TRUE.equals(success);
    }
    
    public void releaseLock(String orderId) {
        redisTemplate.delete(PREFIX + orderId);
    }
}

方案三:使用 Kafka 事务

@Transactional(transactionManager = "kafkaTransactionManager")
public void createOrder(OrderDTO order) {
    // 1. 保存到数据库
    orderRepository.save(order);
    
    // 2. 发送消息到 Kafka
    kafkaTemplate.send("order-topic", order.getOrderId(), order);
    
    // 如果这里抛出异常,数据库回滚,消息也不会发送
}

五、最佳实践总结

5.1 Feign 使用建议

  1. 必须配置 contextId:避免同服务多客户端冲突
  2. 优先使用 fallbackFactory:便于问题排查和精细化降级
  3. 合理配置超时时间:防止长耗时请求阻塞
  4. 开启日志级别:便于调试和排查问题

5.2 自动配置要点

  1. 公共模块使用 AutoConfiguration.imports:实现自动注入
  2. 合理使用条件注解:避免不必要的 Bean 创建
  3. 注意配置类位置:无需在启动类包路径下

5.3 网关设计原则

  1. 单一职责:网关只负责路由、限流、认证
  2. 熔断降级:保护下游服务
  3. 可观测性:接入监控和日志系统

5.4 Kafka 使用建议

  1. 合理设置分区数:根据业务吞吐量调整
  2. 选择合适的消息确认机制:生产环境建议使用 acks=all
  3. 实现幂等性:防止消息重复消费导致数据不一致
  4. 合理配置消费者并发数:不超过分区数