服务集成
1. 服务间调用
1.1 Feign 客户端定义
java
@FeignClient(
name = "product-service",
path = "/products",
fallbackFactory = ProductClientFallback.class
)
public interface ProductClient {
@GetMapping("/{id}")
Product getProduct(@PathVariable Long id);
@GetMapping
List<Product> getProducts(@RequestParam List<Long> ids);
@PutMapping("/{id}/stock")
void updateStock(@PathVariable Long id, @RequestParam Integer quantity);
}
@Component
public class ProductClientFallback implements FallbackFactory<ProductClient> {
@Override
public ProductClient create(Throwable cause) {
return new ProductClient() {
@Override
public Product getProduct(Long id) {
log.warn("获取商品失败: {}", id, cause);
return null;
}
@Override
public List<Product> getProducts(List<Long> ids) {
log.warn("批量获取商品失败", cause);
return Collections.emptyList();
}
@Override
public void updateStock(Long id, Integer quantity) {
log.warn("更新库存失败: {}", id, cause);
}
};
}
}1.2 服务调用示例
java
@Service
public class OrderService {
private final ProductClient productClient;
private final InventoryClient inventoryClient;
private final UserClient userClient;
public OrderDetail getOrderDetail(Long orderId) {
Order order = orderRepository.findById(orderId)
.orElseThrow(() -> new ResourceNotFoundException("订单不存在"));
// 获取用户信息
User user = userClient.getUser(order.getUserId());
// 获取商品信息
List<Long> productIds = order.getItems().stream()
.map(OrderItem::getProductId)
.toList();
List<Product> products = productClient.getProducts(productIds);
return new OrderDetail(order, user, products);
}
}2. 消息队列集成
2.1 订单消息生产
java
@Service
public class OrderMessageService {
private final RabbitTemplate rabbitTemplate;
private static final String ORDER_EXCHANGE = "order.exchange";
private static final String ORDER_CREATED_KEY = "order.created";
private static final String ORDER_PAID_KEY = "order.paid";
public void sendOrderCreatedMessage(Order order) {
OrderMessage message = new OrderMessage(
order.getId(),
order.getOrderNo(),
order.getUserId(),
order.getTotalAmount(),
LocalDateTime.now()
);
rabbitTemplate.convertAndSend(
ORDER_EXCHANGE,
ORDER_CREATED_KEY,
message
);
}
public void sendOrderPaidMessage(Order order) {
OrderMessage message = new OrderMessage(
order.getId(),
order.getOrderNo(),
order.getUserId(),
order.getTotalAmount(),
LocalDateTime.now()
);
rabbitTemplate.convertAndSend(
ORDER_EXCHANGE,
ORDER_PAID_KEY,
message
);
}
}2.2 消息消费
java
@Component
public class OrderMessageConsumer {
private final InventoryService inventoryService;
private final NotificationService notificationService;
@RabbitListener(queues = "order.created.queue")
public void handleOrderCreated(OrderMessage message) {
log.info("收到订单创建消息: {}", message);
// 扣减库存
for (OrderItem item : message.getItems()) {
inventoryService.deductStock(item.getProductId(), item.getQuantity());
}
}
@RabbitListener(queues = "order.paid.queue")
public void handleOrderPaid(OrderMessage message) {
log.info("收到订单支付消息: {}", message);
// 发送通知
notificationService.sendOrderPaidNotification(
message.getUserId(),
message.getOrderNo()
);
}
}3. 分布式事务
3.1 Seata 配置
java
@Configuration
public class SeataConfig {
@Bean
public GlobalTransactionScanner globalTransactionScanner() {
return new GlobalTransactionScanner(
"order-service",
"my_tx_group"
);
}
}3.2 分布式事务示例
java
@Service
public class OrderTransactionService {
private final OrderService orderService;
private final InventoryService inventoryService;
private final PaymentService paymentService;
@GlobalTransactional(name = "create-order-transaction")
public Order createOrderWithTransaction(OrderRequest request) {
// 创建订单
Order order = orderService.create(request);
// 扣减库存
for (OrderItemRequest item : request.getItems()) {
inventoryService.deductStock(item.getProductId(), item.getQuantity());
}
// 处理支付
Payment payment = paymentService.processPayment(
order.getId(),
request.getPaymentMethod()
);
// 更新订单状态
order.setPaymentStatus(PaymentStatus.PAID);
order.setStatus(OrderStatus.CONFIRMED);
return orderService.update(order);
}
}4. 缓存集成
4.1 Redis 缓存配置
java
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(
RedisConnectionFactory connectionFactory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(connectionFactory);
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
template.setHashKeySerializer(new StringRedisSerializer());
template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
return template;
}
@Bean
public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) {
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofMinutes(30))
.serializeKeysWith(RedisSerializationContext.SerializationPair
.fromSerializer(new StringRedisSerializer()))
.serializeValuesWith(RedisSerializationContext.SerializationPair
.fromSerializer(new GenericJackson2JsonRedisSerializer()));
return RedisCacheManager.builder(connectionFactory)
.cacheDefaults(config)
.build();
}
}4.2 缓存使用
java
@Service
public class ProductService {
private final ProductRepository productRepository;
private final RedisTemplate<String, Object> redisTemplate;
private static final String PRODUCT_CACHE_KEY = "product:";
@Cacheable(value = "products", key = "#id")
public Product findById(Long id) {
return productRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("商品不存在"));
}
@CachePut(value = "products", key = "#product.id")
public Product update(Product product) {
return productRepository.save(product);
}
@CacheEvict(value = "products", key = "#id")
public void delete(Long id) {
productRepository.deleteById(id);
}
public Product findByIdWithCache(Long id) {
String key = PRODUCT_CACHE_KEY + id;
Product product = (Product) redisTemplate.opsForValue().get(key);
if (product != null) {
return product;
}
product = productRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("商品不存在"));
redisTemplate.opsForValue().set(key, product, Duration.ofHours(1));
return product;
}
}5. 链路追踪集成
5.1 追踪配置
yaml
management:
tracing:
enabled: true
sampling:
probability: 1.0
zipkin:
tracing:
endpoint: http://localhost:9411/api/v2/spans
spring:
application:
name: order-service5.2 自定义追踪
java
@Service
public class TracedOrderService {
private final Tracer tracer;
private final OrderRepository orderRepository;
public Order createOrder(OrderRequest request) {
Span span = tracer.nextSpan().name("create-order");
try (Tracer.SpanInScope ws = tracer.withSpan(span.start())) {
span.tag("user.id", request.getUserId().toString());
span.tag("item.count", String.valueOf(request.getItems().size()));
span.event("order.creation.started");
Order order = doCreateOrder(request);
span.tag("order.id", order.getId().toString());
span.event("order.creation.completed");
return order;
} catch (Exception e) {
span.event("order.creation.failed");
span.tag("error", e.getMessage());
throw e;
} finally {
span.end();
}
}
}6. 小结
本章完成了服务集成:
| 内容 | 要点 |
|---|---|
| 服务调用 | Feign 客户端、熔断降级 |
| 消息队列 | RabbitMQ、消息生产消费 |
| 分布式事务 | Seata、@GlobalTransactional |
| 缓存集成 | Redis、@Cacheable |
| 链路追踪 | Micrometer Tracing、Zipkin |
下一章将学习项目测试。