0%
Communication entre microservices

Communication entre microservices

Les différentes stratégies de communication

10-15 min

Communication entre microservices

Dans ce chapitre, nous explorerons les différentes stratégies de communication entre microservices. Nous verrons comment implémenter et utiliser chaque approche avec Spring Cloud.

1. Communication REST avec OpenFeign

Configuration

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

Implémentation

@FeignClient(name = "order-service", url = "${order.service.url}")
public interface OrderClient {
    @GetMapping("/api/orders/{id}")
    OrderDTO getOrder(@PathVariable("id") Long id);
    
    @PostMapping("/api/orders")
    OrderDTO createOrder(@RequestBody CreateOrderRequest request);
}

Utilisation

@Service
@RequiredArgsConstructor
public class OrderService {
    private final OrderClient orderClient;
    
    public OrderDTO getOrderDetails(Long orderId) {
        return orderClient.getOrder(orderId);
    }
}

2. Messaging avec RabbitMQ

Configuration

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>

Configuration RabbitMQ

spring:
  rabbitmq:
    host: localhost
    port: 5672
    username: guest
    password: guest

Implémentation du Producteur

@Component
@RequiredArgsConstructor
public class OrderEventProducer {
    private final RabbitTemplate rabbitTemplate;
    
    public void sendOrderCreatedEvent(OrderCreatedEvent event) {
        rabbitTemplate.convertAndSend(
            "order-exchange",
            "order.created",
            event
        );
    }
}

Implémentation du Consommateur

@Component
@RequiredArgsConstructor
public class OrderEventConsumer {
    private final ProductService productService;
    
    @RabbitListener(queues = "order-queue")
    public void handleOrderCreatedEvent(OrderCreatedEvent event) {
        // Traitement de l'événement
        productService.updateStock(event.getProductId(), event.getQuantity());
    }
}

3. Communication gRPC

Configuration

<dependency>
    <groupId>io.grpc</groupId>
    <artifactId>grpc-netty-shaded</artifactId>
    <version>1.42.1</version>
</dependency>

Définition du Service

syntax = "proto3";

package com.example.product;

service ProductService {
    rpc GetProduct (ProductRequest) returns (ProductResponse) {}
    rpc UpdateStock (UpdateStockRequest) returns (UpdateStockResponse) {}
}

message ProductRequest {
    int64 id = 1;
}

message ProductResponse {
    int64 id = 1;
    string name = 2;
    int32 stock = 3;
}

message UpdateStockRequest {
    int64 id = 1;
    int32 quantity = 2;
}

message UpdateStockResponse {
    bool success = 1;
    int32 newStock = 2;
}

Implémentation du Serveur

@GrpcService
public class ProductGrpcService extends ProductServiceGrpc.ProductServiceImplBase {
    private final ProductService productService;
    
    @Override
    public void getProduct(ProductRequest request, StreamObserver<ProductResponse> responseObserver) {
        ProductDTO product = productService.getProductById(request.getId());
        
        ProductResponse response = ProductResponse.newBuilder()
            .setId(product.getId())
            .setName(product.getName())
            .setStock(product.getStock())
            .build();
            
        responseObserver.onNext(response);
        responseObserver.onCompleted();
    }
}

Implémentation du Client

@Component
public class ProductGrpcClient {
    private final ManagedChannel channel;
    private final ProductServiceGrpc.ProductServiceBlockingStub blockingStub;
    
    public ProductGrpcClient(@Value("${grpc.server.host}") String host,
                            @Value("${grpc.server.port}") int port) {
        channel = ManagedChannelBuilder.forAddress(host, port)
            .usePlaintext()
            .build();
        blockingStub = ProductServiceGrpc.newBlockingStub(channel);
    }
    
    public ProductResponse getProduct(Long id) {
        ProductRequest request = ProductRequest.newBuilder()
            .setId(id)
            .build();
        return blockingStub.getProduct(request);
    }
}

4. Communication avec Apache Kafka

Configuration

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

Configuration Kafka

spring:
  kafka:
    bootstrap-servers: localhost:9092
    consumer:
      group-id: product-group
      auto-offset-reset: earliest
    producer:
      key-serializer: org.apache.kafka.common.serialization.StringSerializer
      value-serializer: org.springframework.kafka.support.serializer.JsonSerializer

Implémentation du Producteur

@Component
@RequiredArgsConstructor
public class KafkaProducer {
    private final KafkaTemplate<String, OrderEvent> kafkaTemplate;
    
    public void sendOrderEvent(OrderEvent event) {
        kafkaTemplate.send(
            "order-events",
            event.getOrderId().toString(),
            event
        );
    }
}

Implémentation du Consommateur

@Component
@RequiredArgsConstructor
public class KafkaConsumer {
    private final ProductService productService;
    
    @KafkaListener(topics = "order-events", groupId = "product-group")
    public void handleOrderEvent(OrderEvent event) {
        switch (event.getType()) {
            case ORDER_CREATED:
                productService.updateStock(event.getProductId(), -event.getQuantity());
                break;
            case ORDER_CANCELLED:
                productService.updateStock(event.getProductId(), event.getQuantity());
                break;
        }
    }
}

5. Communication avec WebSocket

Configuration

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-websocket</artifactId>
</dependency>

Configuration WebSocket

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
    
    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {
        config.enableSimpleBroker("/topic");
        config.setApplicationDestinationPrefixes("/app");
    }
    
    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/ws")
            .setAllowedOrigins("*")
            .withSockJS();
    }
}

Implémentation du Service

@Service
@RequiredArgsConstructor
public class NotificationService {
    private final SimpMessagingTemplate messagingTemplate;
    
    public void sendStockUpdate(Long productId, int newStock) {
        StockUpdate update = new StockUpdate(productId, newStock);
        messagingTemplate.convertAndSend(
            "/topic/stock-updates",
            update
        );
    }
}

Client WebSocket

const socket = new SockJS('/ws');
const stompClient = Stomp.over(socket);

stompClient.connect({}, function(frame) {
    stompClient.subscribe('/topic/stock-updates', function(update) {
        const stockUpdate = JSON.parse(update.body);
        updateUI(stockUpdate);
    });
});

6. Patterns de Communication

1. Synchronous vs Asynchronous

Communication Synchrone

  • REST avec OpenFeign
  • gRPC
  • Avantages :
    • Simplicité
    • Réponse immédiate
  • Inconvénients :
    • Couplage temporel
    • Blocage des ressources

Communication Asynchrone

  • RabbitMQ
  • Kafka
  • WebSocket
  • Avantages :
    • Découplage
    • Scalabilité
    • Résilience
  • Inconvénients :
    • Complexité
    • Gestion des erreurs

2. Patterns de Communication

Request-Response

// REST
@GetMapping("/api/products/{id}")
public ProductDTO getProduct(@PathVariable Long id) {
    return productService.getProduct(id);
}

// gRPC
rpc GetProduct (ProductRequest) returns (ProductResponse) {}

Event-Driven

// RabbitMQ
@RabbitListener(queues = "order-queue")
public void handleOrderCreatedEvent(OrderCreatedEvent event) {
    // Traitement
}

// Kafka
@KafkaListener(topics = "order-events")
public void handleOrderEvent(OrderEvent event) {
    // Traitement
}

Publish-Subscribe

// WebSocket
messagingTemplate.convertAndSend("/topic/stock-updates", update);

Exercices pratiques

  1. Implémentation d’un système de notification

    • Créez un service de notification
    • Implémentez la communication avec WebSocket
    • Ajoutez la gestion des erreurs
  2. Système de commande asynchrone

    • Créez un service de commande
    • Implémentez la communication avec RabbitMQ
    • Gérer les retries et les dead letters
  3. Streaming de données

    • Créez un service de monitoring
    • Implémentez la communication avec Kafka
    • Ajoutez le traitement en temps réel

Conclusion

Dans ce chapitre, nous avons exploré différentes stratégies de communication entre microservices :

  • REST avec OpenFeign
  • Messaging avec RabbitMQ
  • Communication gRPC
  • Communication avec Kafka
  • WebSocket

Chaque approche a ses avantages et ses cas d’utilisation spécifiques. Le choix dépend des besoins de votre application en termes de :

  • Performance
  • Scalabilité
  • Fiabilité
  • Complexité

Dans le prochain chapitre, nous explorerons la découverte de services avec Eureka.

Commentaires

Les commentaires sont alimentés par GitHub Discussions

Connectez-vous avec GitHub pour participer à la discussion

Lien copié !