微服务Java分类指南
一、OpenFeign 远程调用
1.1 核心注解 @FeignClient
@FeignClient 是 Spring Cloud OpenFeign 的核心注解,用于声明远程 HTTP 调用客户端。
核心属性详解
| 属性 | 作用 | 默认值 |
|---|---|---|
value/name | 指定目标微服务名称 | "" |
contextId | Feign 上下文唯一标识,解决同服务多客户端冲突 | "" |
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 使用建议
- 必须配置 contextId:避免同服务多客户端冲突
- 优先使用 fallbackFactory:便于问题排查和精细化降级
- 合理配置超时时间:防止长耗时请求阻塞
- 开启日志级别:便于调试和排查问题
5.2 自动配置要点
- 公共模块使用 AutoConfiguration.imports:实现自动注入
- 合理使用条件注解:避免不必要的 Bean 创建
- 注意配置类位置:无需在启动类包路径下
5.3 网关设计原则
- 单一职责:网关只负责路由、限流、认证
- 熔断降级:保护下游服务
- 可观测性:接入监控和日志系统
5.4 Kafka 使用建议
- 合理设置分区数:根据业务吞吐量调整
- 选择合适的消息确认机制:生产环境建议使用 acks=all
- 实现幂等性:防止消息重复消费导致数据不一致
- 合理配置消费者并发数:不超过分区数