๐ Introduction
In a microservices architecture, services often need to communicate with each other. While synchronous communication using REST APIs is common, asynchronous communication using Kafka is preferred for event-driven and loosely coupled systems.
In this blog, we’ll explore how to implement service-to-service communication using Apache Kafka and Spring Cloud Stream in Spring Boot applications.
๐งฉ Tech Stack
- Java 17+
- Spring Boot 3.x
- Spring Cloud Stream
- Apache Kafka
- Spring Cloud Function (Optional)
๐ฆ Project Overview
We'll build two microservices:
- Order Service – Publishes events to Kafka when a new order is created.
- Inventory Service – Listens to those events and updates the stock.
๐ Dependencies (Add to both services)
<!-- pom.xml -->
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-stream-kafka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
Add the Spring Cloud BOM in pom.xml
:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>2023.0.1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
๐งพ 1. Order Service (Producer)
✅ application.yml
spring:
application:
name: order-service
cloud:
stream:
bindings:
order-out:
destination: order-topic
content-type: application/json
producer:
required-groups: inventory-group
kafka:
binder:
brokers: localhost:9092
✅ Order Model
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Order {
private String orderId;
private String productId;
private int quantity;
}
✅ Order Publisher
@Service
@RequiredArgsConstructor
public class OrderService {
private final StreamBridge streamBridge;
public void placeOrder(Order order) {
streamBridge.send("order-out", order);
System.out.println("๐ค Sent Order to Kafka: " + order);
}
}
✅ REST Controller
@RestController
@RequestMapping("/orders")
@RequiredArgsConstructor
public class OrderController {
private final OrderService orderService;
@PostMapping
public ResponseEntity<String> createOrder(@RequestBody Order order) {
orderService.placeOrder(order);
return ResponseEntity.ok("Order placed successfully!");
}
}
๐ฆ 2. Inventory Service (Consumer)
✅ application.yml
spring:
application:
name: inventory-service
cloud:
stream:
bindings:
order-in:
destination: order-topic
group: inventory-group
content-type: application/json
kafka:
binder:
brokers: localhost:9092
✅ Inventory Listener
@Component
public class InventoryConsumer {
@StreamListener("order-in")
public void handleOrder(Order order) {
System.out.println("๐ฅ Received Order in Inventory: " + order);
// Simulate inventory update
System.out.println("✅ Inventory updated for product: " + order.getProductId());
}
}
✅ You can also use
@Bean
with functional style instead of@StreamListener
:
@Bean
public Consumer<Order> orderConsumer() {
return order -> {
System.out.println("๐ฅ [Functional] Received Order: " + order);
};
}
๐ ️ Kafka Setup (Local)
- Start Zookeeper & Kafka:
# Start Zookeeper
bin/zookeeper-server-start.sh config/zookeeper.properties
# Start Kafka Server
bin/kafka-server-start.sh config/server.properties
- Create Topic:
bin/kafka-topics.sh --create --topic order-topic --bootstrap-server localhost:9092 --partitions 1 --replication-factor 1
๐งช Testing
- Run both services.
- Make a POST call to
localhost:8080/orders
:
POST /orders
Content-Type: application/json
{
"orderId": "O101",
"productId": "P5001",
"quantity": 3
}
- Output:
- Order Service:
๐ค Sent Order to Kafka
- Inventory Service:
๐ฅ Received Order in Inventory
๐ง Why Use Spring Cloud Stream?
Feature | Benefit |
---|---|
Abstraction over Kafka | Write less boilerplate code. |
Flexible Binding | Easily switch between Kafka, RabbitMQ, etc. |
Declarative Configuration | Use YAML to configure consumers/producers. |
Built-in Serialization | JSON, Avro, etc. with minimal setup. |
⚖️ Kafka vs REST for Microservices
REST (Synchronous) | Kafka (Asynchronous) |
---|---|
Simple & direct | Decoupled services |
Tight coupling | Loose coupling |
Fails if target down | Can retry later |
Real-time request/response | Event-driven patterns |
✅ Best Practices
- ✅ Always define a consumer group.
- ✅ Handle serialization errors using
ErrorHandlingDeserializer
. - ✅ Use dead-letter queues for failed messages.
- ✅ Monitor with Kafka Manager or Confluent Control Center.
- ✅ Use Avro/Schema Registry for contract enforcement.
๐ Conclusion
Using Spring Cloud Stream with Kafka is a powerful way to enable asynchronous service-to-service communication. It abstracts low-level Kafka configurations and allows you to focus on business logic.
๐ Whether you're building event-driven microservices or improving scalability, this architecture ensures decoupling, flexibility, and resilience.
\
Tidak ada komentar:
Posting Komentar