Skip to content

服务集成

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-service

5.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

下一章将学习项目测试。