├── .circleci └── config.yml ├── .settings └── .gitignore ├── README.md ├── all-order-processor ├── .gitignore ├── pom.xml └── src │ └── main │ ├── java │ └── pl │ │ └── piomin │ │ └── services │ │ └── vertx │ │ └── order │ │ ├── AllOrderProcessorVerticle.java │ │ └── model │ │ ├── Order.java │ │ ├── OrderStatus.java │ │ └── OrderType.java │ └── resources │ └── log4j.xml ├── mulpiple-order-processor ├── .gitignore ├── .settings │ └── .gitignore ├── pom.xml └── src │ └── main │ ├── java │ └── pl │ │ └── piomin │ │ └── services │ │ └── vertx │ │ └── order │ │ ├── MultipleOrderProcessorVerticle.java │ │ └── model │ │ ├── Order.java │ │ ├── OrderStatus.java │ │ └── OrderType.java │ └── resources │ └── log4j.xml ├── order-processor ├── .gitignore ├── .settings │ └── .gitignore ├── pom.xml └── src │ └── main │ ├── java │ └── pl │ │ └── piomin │ │ └── services │ │ └── vertx │ │ └── order │ │ ├── SingleOrderProcessorVerticle.java │ │ └── model │ │ ├── Order.java │ │ ├── OrderStatus.java │ │ └── OrderType.java │ └── resources │ └── log4j.xml ├── order-service ├── .gitignore ├── .settings │ └── .gitignore ├── pom.xml └── src │ └── main │ ├── java │ └── pl │ │ └── piomin │ │ └── services │ │ └── vertx │ │ └── order │ │ ├── OrderVerticle.java │ │ └── model │ │ ├── Order.java │ │ ├── OrderStatus.java │ │ └── OrderType.java │ └── resources │ └── log4j.xml ├── pom.xml └── renovate.json /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2.1 2 | 3 | jobs: 4 | analyze: 5 | docker: 6 | - image: 'cimg/openjdk:21.0.9' 7 | steps: 8 | - checkout 9 | - run: 10 | name: Analyze on SonarCloud 11 | command: mvn verify sonar:sonar 12 | 13 | executors: 14 | jdk: 15 | docker: 16 | - image: 'cimg/openjdk:21.0.9' 17 | 18 | orbs: 19 | maven: circleci/maven@2.1.1 20 | 21 | workflows: 22 | maven_test: 23 | jobs: 24 | - maven/test: 25 | executor: jdk 26 | verify_dependencies: false 27 | - analyze: 28 | context: SonarCloud -------------------------------------------------------------------------------- /.settings/.gitignore: -------------------------------------------------------------------------------- 1 | /org.eclipse.core.resources.prefs 2 | /org.eclipse.jdt.core.prefs 3 | /org.eclipse.m2e.core.prefs 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Partitioning with Apache Kafka and Vert.x 2 | 3 | Detailed description can be found here: [Partitioning with Apache Kafka and Vert.x](https://piotrminkowski.com/2018/01/30/partitioning-with-apache-kafka-and-vertx/) 4 | 5 | # Vert.x + Kafka — Order Processing Demo 6 | 7 | A demonstration of using Eclipse Vert.x with Apache Kafka for an order processing pipeline. The `order-service` module exposes a REST endpoint to submit orders, which are sent to a Kafka topic; multiple consumer verticles process orders from different partitions to illustrate ordered and parallel consumption. 8 | 9 | ## 🗂️ Modules 10 | | Path | Description | 11 | |----------------------------|---------------------------------------| 12 | | `order-service` | REST API → produces to **orders-out** | 13 | | `order-processor` | Consumes partition 0 | 14 | | `multiple-order-processor` | Consumes partition 1 | 15 | | `all-order-processor` | Consumes all partitions | 16 | 17 | ## ✅ Prerequisites 18 | - Java 21 19 | - Maven 3.9+ 20 | - Running Kafka broker (defaults to `192.168.99.100:9092`) 21 | 22 | ## ⚡ Quick Start 23 | ```bash 24 | # Start Zookeeper (if needed) 25 | docker run -d --name zookeeper -p 2181:2181 zookeeper:3.5 26 | 27 | # Start single-node Kafka 28 | docker run -d --name kafka -p 9092:9092 \ 29 | --link zookeeper:zookeeper \ 30 | -e KAFKA_ZOOKEEPER_CONNECT=zookeeper:2181 \ 31 | -e KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://192.168.99.100:9092 \ 32 | -e KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR=1 \ 33 | confluentinc/cp-kafka:latest 34 | 35 | # Build all modules 36 | mvn clean package 37 | 38 | # Run verticles 39 | java -jar order-service/target/order-service-*.jar 40 | java -jar order-processor/target/order-processor-*.jar 41 | java -jar multiple-order-processor/target/multiple-order-processor-*.jar 42 | java -jar all-order-processor/target/all-order-processor-*.jar 43 | ``` 44 | 45 | ## 🏗️ Architecture 46 | ```mermaid 47 | flowchart LR 48 | Client --> OS[order-service] 49 | OS -->|JSON order| KT["Kafka
orders-out"] 50 | subgraph Consumers 51 | SOP["order-processor
partition 0"] 52 | MOP["multiple-order-processor
partition 1"] 53 | AOP["all-order-processor
all partitions"] 54 | end 55 | KT --> SOP 56 | KT --> MOP 57 | KT --> AOP 58 | ``` 59 | 60 | ## 🔧 Configuration 61 | | Key | Default | How to override | 62 | |----------------------|------------------------|-------------------------------| 63 | | `bootstrap.servers` | `192.168.99.100:9092` | `env KAFKA_BOOTSTRAP_SERVERS` | 64 | | `topic` | `orders-out` | `env KAFKA_TOPIC` | 65 | 66 | ## 🔍 Testing 67 | Consume messages from the `orders-out` topic using the Kafka console consumer: 68 | ```bash 69 | kafka-console-consumer --bootstrap-server 192.168.99.100:9092 \ 70 | --topic orders-out --from-beginning 71 | ``` 72 | 73 | ## 📜 License 74 | MIT © Piotr Mińkowski 75 | -------------------------------------------------------------------------------- /all-order-processor/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | /.classpath 3 | /.project 4 | /.settings/ 5 | -------------------------------------------------------------------------------- /all-order-processor/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | pl.piomin.services 6 | sample-vertx-kafka-messaging 7 | 1.0-SNAPSHOT 8 | 9 | all-order-processor 10 | 11 | 12 | ${project.artifactId} 13 | 14 | 15 | 16 | 17 | io.vertx 18 | vertx-kafka-client 19 | ${vertx.version} 20 | 21 | 22 | 23 | 24 | 25 | 26 | org.apache.maven.plugins 27 | maven-compiler-plugin 28 | 3.14.1 29 | 30 | ${project.build.sourceEncoding} 31 | ${java.version} 32 | ${java.version} 33 | 34 | 35 | 36 | org.apache.maven.plugins 37 | maven-shade-plugin 38 | 39 | 40 | 41 | shade 42 | 43 | 44 | true 45 | 46 | 48 | pl.piomin.services.vertx.order.AllOrderProcessorVerticle 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /all-order-processor/src/main/java/pl/piomin/services/vertx/order/AllOrderProcessorVerticle.java: -------------------------------------------------------------------------------- 1 | package pl.piomin.services.vertx.order; 2 | 3 | import java.util.Properties; 4 | 5 | import org.apache.kafka.clients.consumer.ConsumerConfig; 6 | import org.apache.kafka.common.serialization.StringDeserializer; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | import io.vertx.core.AbstractVerticle; 11 | import io.vertx.core.Vertx; 12 | import io.vertx.core.json.Json; 13 | import io.vertx.kafka.client.consumer.KafkaConsumer; 14 | import pl.piomin.services.vertx.order.model.Order; 15 | import pl.piomin.services.vertx.order.model.OrderStatus; 16 | 17 | public class AllOrderProcessorVerticle extends AbstractVerticle { 18 | 19 | private static final Logger LOGGER = LoggerFactory.getLogger(AllOrderProcessorVerticle.class); 20 | 21 | public static void main(String[] args) { 22 | Vertx vertx = Vertx.vertx(); 23 | vertx.deployVerticle(new AllOrderProcessorVerticle()); 24 | } 25 | 26 | @Override 27 | public void start() throws Exception { 28 | Properties config = new Properties(); 29 | config.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "192.168.99.100:9092"); 30 | config.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); 31 | config.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); 32 | config.put(ConsumerConfig.GROUP_ID_CONFIG, "all-order"); 33 | config.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "latest"); 34 | config.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, "false"); 35 | KafkaConsumer consumer = KafkaConsumer.create(vertx, config); 36 | consumer.subscribe("orders-out", ar -> { 37 | if (ar.succeeded()) { 38 | LOGGER.info("Subscribed"); 39 | } else { 40 | LOGGER.error("Could not subscribe: err={}", ar.cause().getMessage()); 41 | } 42 | }); 43 | 44 | consumer.handler(record -> { 45 | LOGGER.info("Processing: key={}, value={}, partition={}, offset={}", record.key(), record.value(), record.partition(), record.offset()); 46 | Order order = Json.decodeValue(record.value(), Order.class); 47 | order.setStatus(OrderStatus.DONE); 48 | LOGGER.info("Order processed: id={}, price={}", order.getId(), order.getPrice()); 49 | }); 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /all-order-processor/src/main/java/pl/piomin/services/vertx/order/model/Order.java: -------------------------------------------------------------------------------- 1 | package pl.piomin.services.vertx.order.model; 2 | 3 | public class Order { 4 | 5 | private Long id; 6 | private OrderType type; 7 | private OrderStatus status; 8 | private int price; 9 | private Long relatedOrderId; 10 | 11 | public Long getId() { 12 | return id; 13 | } 14 | 15 | public void setId(Long id) { 16 | this.id = id; 17 | } 18 | 19 | public OrderType getType() { 20 | return type; 21 | } 22 | 23 | public void setType(OrderType type) { 24 | this.type = type; 25 | } 26 | 27 | public OrderStatus getStatus() { 28 | return status; 29 | } 30 | 31 | public void setStatus(OrderStatus status) { 32 | this.status = status; 33 | } 34 | 35 | public int getPrice() { 36 | return price; 37 | } 38 | 39 | public void setPrice(int price) { 40 | this.price = price; 41 | } 42 | 43 | public Long getRelatedOrderId() { 44 | return relatedOrderId; 45 | } 46 | 47 | public void setRelatedOrderId(Long relatedOrderId) { 48 | this.relatedOrderId = relatedOrderId; 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /all-order-processor/src/main/java/pl/piomin/services/vertx/order/model/OrderStatus.java: -------------------------------------------------------------------------------- 1 | package pl.piomin.services.vertx.order.model; 2 | 3 | public enum OrderStatus { 4 | 5 | NEW, PROCESSING, DONE, REJECTED; 6 | 7 | } 8 | -------------------------------------------------------------------------------- /all-order-processor/src/main/java/pl/piomin/services/vertx/order/model/OrderType.java: -------------------------------------------------------------------------------- 1 | package pl.piomin.services.vertx.order.model; 2 | 3 | public enum OrderType { 4 | 5 | SINGLE, MULTIPLE; 6 | 7 | } 8 | -------------------------------------------------------------------------------- /all-order-processor/src/main/resources/log4j.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /mulpiple-order-processor/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | /.classpath 3 | /.project 4 | -------------------------------------------------------------------------------- /mulpiple-order-processor/.settings/.gitignore: -------------------------------------------------------------------------------- 1 | /org.eclipse.core.resources.prefs 2 | /org.eclipse.jdt.core.prefs 3 | /org.eclipse.m2e.core.prefs 4 | -------------------------------------------------------------------------------- /mulpiple-order-processor/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | pl.piomin.services 6 | sample-vertx-kafka-messaging 7 | 1.0-SNAPSHOT 8 | 9 | mulpiple-order-processor 10 | 11 | 12 | ${project.artifactId} 13 | 14 | 15 | 16 | 17 | io.vertx 18 | vertx-kafka-client 19 | ${vertx.version} 20 | 21 | 22 | 23 | 24 | 25 | 26 | org.apache.maven.plugins 27 | maven-compiler-plugin 28 | 3.14.1 29 | 30 | ${project.build.sourceEncoding} 31 | ${java.version} 32 | ${java.version} 33 | 34 | 35 | 36 | org.apache.maven.plugins 37 | maven-shade-plugin 38 | 39 | 40 | 41 | shade 42 | 43 | 44 | true 45 | 46 | 48 | pl.piomin.services.vertx.order.MultipleOrderProcessorVerticle 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /mulpiple-order-processor/src/main/java/pl/piomin/services/vertx/order/MultipleOrderProcessorVerticle.java: -------------------------------------------------------------------------------- 1 | package pl.piomin.services.vertx.order; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | import java.util.Properties; 6 | 7 | import org.apache.kafka.clients.consumer.ConsumerConfig; 8 | import org.apache.kafka.common.serialization.StringDeserializer; 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | 12 | import io.vertx.core.AbstractVerticle; 13 | import io.vertx.core.Vertx; 14 | import io.vertx.core.json.Json; 15 | import io.vertx.kafka.client.common.TopicPartition; 16 | import io.vertx.kafka.client.consumer.KafkaConsumer; 17 | import pl.piomin.services.vertx.order.model.Order; 18 | 19 | public class MultipleOrderProcessorVerticle extends AbstractVerticle { 20 | 21 | private static final Logger LOGGER = LoggerFactory.getLogger(MultipleOrderProcessorVerticle.class); 22 | 23 | private Map ordersWaiting = new HashMap<>(); 24 | 25 | public static void main(String[] args) { 26 | Vertx vertx = Vertx.vertx(); 27 | vertx.deployVerticle(new MultipleOrderProcessorVerticle()); 28 | } 29 | 30 | @Override 31 | public void start() throws Exception { 32 | Properties config = new Properties(); 33 | config.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "192.168.99.100:9092"); 34 | config.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); 35 | config.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); 36 | config.put(ConsumerConfig.GROUP_ID_CONFIG, "multiple-order"); 37 | config.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "latest"); 38 | config.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, "false"); 39 | TopicPartition tp = new TopicPartition().setPartition(1).setTopic("orders-out"); 40 | KafkaConsumer consumer = KafkaConsumer.create(vertx, config); 41 | consumer.assign(tp, ar -> { 42 | if (ar.succeeded()) { 43 | LOGGER.info("Subscribed"); 44 | consumer.assignment(done1 -> { 45 | if (done1.succeeded()) { 46 | for (TopicPartition topicPartition : done1.result()) { 47 | LOGGER.info("Partition: topic={}, number={}", topicPartition.getTopic(), topicPartition.getPartition()); 48 | } 49 | } else { 50 | LOGGER.error("Could not assign partition: err={}", done1.cause().getMessage()); 51 | } 52 | }); 53 | } else { 54 | LOGGER.error("Could not subscribe: err={}", ar.cause().getMessage()); 55 | } 56 | }); 57 | 58 | consumer.handler(record -> { 59 | LOGGER.info("Processing: key={}, value={}, partition={}, offset={}", record.key(), record.value(), record.partition(), record.offset()); 60 | Order order = Json.decodeValue(record.value(), Order.class); 61 | if (ordersWaiting.containsKey(record.offset())) { 62 | LOGGER.info("Related order found: id={}, price={}", order.getId(), order.getPrice()); 63 | LOGGER.info("Current price: price={}", order.getPrice() + ordersWaiting.get(record.offset()).getPrice()); 64 | consumer.seekToEnd(tp); 65 | } 66 | 67 | if (order.getRelatedOrderId() != null && !ordersWaiting.containsKey(order.getRelatedOrderId())) { 68 | ordersWaiting.put(order.getRelatedOrderId(), order); 69 | consumer.seek(tp, order.getRelatedOrderId()); 70 | } 71 | }); 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /mulpiple-order-processor/src/main/java/pl/piomin/services/vertx/order/model/Order.java: -------------------------------------------------------------------------------- 1 | package pl.piomin.services.vertx.order.model; 2 | 3 | public class Order { 4 | 5 | private Long id; 6 | private OrderType type; 7 | private OrderStatus status; 8 | private int price; 9 | private Long relatedOrderId; 10 | 11 | public Long getId() { 12 | return id; 13 | } 14 | 15 | public void setId(Long id) { 16 | this.id = id; 17 | } 18 | 19 | public OrderType getType() { 20 | return type; 21 | } 22 | 23 | public void setType(OrderType type) { 24 | this.type = type; 25 | } 26 | 27 | public OrderStatus getStatus() { 28 | return status; 29 | } 30 | 31 | public void setStatus(OrderStatus status) { 32 | this.status = status; 33 | } 34 | 35 | public int getPrice() { 36 | return price; 37 | } 38 | 39 | public void setPrice(int price) { 40 | this.price = price; 41 | } 42 | 43 | public Long getRelatedOrderId() { 44 | return relatedOrderId; 45 | } 46 | 47 | public void setRelatedOrderId(Long relatedOrderId) { 48 | this.relatedOrderId = relatedOrderId; 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /mulpiple-order-processor/src/main/java/pl/piomin/services/vertx/order/model/OrderStatus.java: -------------------------------------------------------------------------------- 1 | package pl.piomin.services.vertx.order.model; 2 | 3 | public enum OrderStatus { 4 | 5 | NEW, PROCESSING, DONE, REJECTED; 6 | 7 | } 8 | -------------------------------------------------------------------------------- /mulpiple-order-processor/src/main/java/pl/piomin/services/vertx/order/model/OrderType.java: -------------------------------------------------------------------------------- 1 | package pl.piomin.services.vertx.order.model; 2 | 3 | public enum OrderType { 4 | 5 | SINGLE, MULTIPLE; 6 | 7 | } 8 | -------------------------------------------------------------------------------- /mulpiple-order-processor/src/main/resources/log4j.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /order-processor/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | /.classpath 3 | /.project 4 | -------------------------------------------------------------------------------- /order-processor/.settings/.gitignore: -------------------------------------------------------------------------------- 1 | /org.eclipse.core.resources.prefs 2 | /org.eclipse.jdt.core.prefs 3 | /org.eclipse.m2e.core.prefs 4 | -------------------------------------------------------------------------------- /order-processor/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | pl.piomin.services 6 | sample-vertx-kafka-messaging 7 | 1.0-SNAPSHOT 8 | 9 | order-processor 10 | 11 | 12 | ${project.artifactId} 13 | 14 | 15 | 16 | 17 | io.vertx 18 | vertx-kafka-client 19 | ${vertx.version} 20 | 21 | 22 | 23 | 24 | 25 | 26 | org.apache.maven.plugins 27 | maven-compiler-plugin 28 | 3.14.1 29 | 30 | ${project.build.sourceEncoding} 31 | ${java.version} 32 | ${java.version} 33 | 34 | 35 | 36 | org.apache.maven.plugins 37 | maven-shade-plugin 38 | 39 | 40 | 41 | shade 42 | 43 | 44 | true 45 | 46 | 48 | pl.piomin.services.vertx.order.SingleOrderProcessorVerticle 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /order-processor/src/main/java/pl/piomin/services/vertx/order/SingleOrderProcessorVerticle.java: -------------------------------------------------------------------------------- 1 | package pl.piomin.services.vertx.order; 2 | 3 | import java.util.Properties; 4 | 5 | import org.apache.kafka.clients.consumer.ConsumerConfig; 6 | import org.apache.kafka.common.serialization.StringDeserializer; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | import io.vertx.core.AbstractVerticle; 11 | import io.vertx.core.Vertx; 12 | import io.vertx.kafka.client.common.TopicPartition; 13 | import io.vertx.kafka.client.consumer.KafkaConsumer; 14 | 15 | public class SingleOrderProcessorVerticle extends AbstractVerticle { 16 | 17 | private static final Logger LOGGER = LoggerFactory.getLogger(SingleOrderProcessorVerticle.class); 18 | 19 | public static void main(String[] args) { 20 | Vertx vertx = Vertx.vertx(); 21 | vertx.deployVerticle(new SingleOrderProcessorVerticle()); 22 | } 23 | 24 | @Override 25 | public void start() throws Exception { 26 | Properties config = new Properties(); 27 | config.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "192.168.99.100:9092"); 28 | config.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); 29 | config.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); 30 | config.put(ConsumerConfig.GROUP_ID_CONFIG, "single-order"); 31 | config.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "latest"); 32 | config.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, "false"); 33 | TopicPartition tp = new TopicPartition().setPartition(0).setTopic("orders-out"); 34 | KafkaConsumer consumer = KafkaConsumer.create(vertx, config); 35 | consumer.assign(tp, ar -> { 36 | if (ar.succeeded()) { 37 | LOGGER.info("Subscribed"); 38 | consumer.assignment(done1 -> { 39 | if (done1.succeeded()) { 40 | for (TopicPartition topicPartition : done1.result()) { 41 | LOGGER.info("Partition: topic={}, number={}", topicPartition.getTopic(), topicPartition.getPartition()); 42 | } 43 | } 44 | }); 45 | } else { 46 | LOGGER.error("Could not subscribe: err={}", ar.cause().getMessage()); 47 | } 48 | }); 49 | 50 | consumer.handler(record -> { 51 | LOGGER.info("Processing: key={}, value={}, partition={}, offset={}", record.key(), record.value(), record.partition(), record.offset()); 52 | }); 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /order-processor/src/main/java/pl/piomin/services/vertx/order/model/Order.java: -------------------------------------------------------------------------------- 1 | package pl.piomin.services.vertx.order.model; 2 | 3 | public class Order { 4 | 5 | private Long id; 6 | private OrderType type; 7 | private OrderStatus status; 8 | private int price; 9 | 10 | public Long getId() { 11 | return id; 12 | } 13 | 14 | public void setId(Long id) { 15 | this.id = id; 16 | } 17 | 18 | public OrderType getType() { 19 | return type; 20 | } 21 | 22 | public void setType(OrderType type) { 23 | this.type = type; 24 | } 25 | 26 | public OrderStatus getStatus() { 27 | return status; 28 | } 29 | 30 | public void setStatus(OrderStatus status) { 31 | this.status = status; 32 | } 33 | 34 | public int getPrice() { 35 | return price; 36 | } 37 | 38 | public void setPrice(int price) { 39 | this.price = price; 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /order-processor/src/main/java/pl/piomin/services/vertx/order/model/OrderStatus.java: -------------------------------------------------------------------------------- 1 | package pl.piomin.services.vertx.order.model; 2 | 3 | public enum OrderStatus { 4 | 5 | NEW, PROCESSING, DONE, REJECTED; 6 | 7 | } 8 | -------------------------------------------------------------------------------- /order-processor/src/main/java/pl/piomin/services/vertx/order/model/OrderType.java: -------------------------------------------------------------------------------- 1 | package pl.piomin.services.vertx.order.model; 2 | 3 | public enum OrderType { 4 | 5 | SINGLE, MULTIPLE; 6 | 7 | } 8 | -------------------------------------------------------------------------------- /order-processor/src/main/resources/log4j.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /order-service/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | /.classpath 3 | /.project 4 | -------------------------------------------------------------------------------- /order-service/.settings/.gitignore: -------------------------------------------------------------------------------- 1 | /org.eclipse.core.resources.prefs 2 | /org.eclipse.jdt.core.prefs 3 | /org.eclipse.m2e.core.prefs 4 | -------------------------------------------------------------------------------- /order-service/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | pl.piomin.services 6 | sample-vertx-kafka-messaging 7 | 1.0-SNAPSHOT 8 | 9 | order-service 10 | 11 | 12 | ${project.artifactId} 13 | 14 | 15 | 16 | 17 | io.vertx 18 | vertx-kafka-client 19 | ${vertx.version} 20 | 21 | 22 | io.vertx 23 | vertx-web 24 | ${vertx.version} 25 | 26 | 27 | 28 | 29 | 30 | 31 | org.apache.maven.plugins 32 | maven-compiler-plugin 33 | 3.14.1 34 | 35 | ${project.build.sourceEncoding} 36 | ${java.version} 37 | ${java.version} 38 | 39 | 40 | 41 | org.apache.maven.plugins 42 | maven-shade-plugin 43 | 44 | 45 | 46 | shade 47 | 48 | 49 | true 50 | 51 | 53 | pl.piomin.services.vertx.order.OrderVerticle 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /order-service/src/main/java/pl/piomin/services/vertx/order/OrderVerticle.java: -------------------------------------------------------------------------------- 1 | package pl.piomin.services.vertx.order; 2 | 3 | import io.vertx.core.AbstractVerticle; 4 | import io.vertx.core.Vertx; 5 | import io.vertx.core.http.HttpMethod; 6 | import io.vertx.core.json.Json; 7 | import io.vertx.core.json.JsonObject; 8 | import io.vertx.ext.web.Router; 9 | import io.vertx.ext.web.handler.BodyHandler; 10 | import io.vertx.ext.web.handler.ResponseContentTypeHandler; 11 | import io.vertx.kafka.client.producer.KafkaProducer; 12 | import io.vertx.kafka.client.producer.KafkaProducerRecord; 13 | import io.vertx.kafka.client.serialization.JsonObjectSerializer; 14 | import org.apache.kafka.clients.producer.ProducerConfig; 15 | import org.apache.kafka.common.serialization.StringSerializer; 16 | import org.slf4j.Logger; 17 | import org.slf4j.LoggerFactory; 18 | import pl.piomin.services.vertx.order.model.Order; 19 | import pl.piomin.services.vertx.order.model.OrderStatus; 20 | 21 | import java.util.Properties; 22 | 23 | public class OrderVerticle extends AbstractVerticle { 24 | 25 | private static final Logger LOGGER = LoggerFactory.getLogger(OrderVerticle.class); 26 | 27 | public static void main(String[] args) { 28 | Vertx vertx = Vertx.vertx(); 29 | vertx.deployVerticle(new OrderVerticle()); 30 | } 31 | 32 | @Override 33 | public void start() throws Exception { 34 | Properties config = new Properties(); 35 | config.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "192.168.99.100:9092"); 36 | config.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class); 37 | config.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, JsonObjectSerializer.class); 38 | config.put(ProducerConfig.ACKS_CONFIG, "1"); 39 | KafkaProducer producer = KafkaProducer.create(vertx, config); 40 | producer.partitionsFor("orders-out", done -> { 41 | done.result().forEach(p -> LOGGER.info("Partition: id={}, topic={}", p.getPartition(), p.getTopic())); 42 | }); 43 | 44 | Router router = Router.router(vertx); 45 | router.route("/order/*").handler(ResponseContentTypeHandler.create()); 46 | router.route(HttpMethod.POST, "/order").handler(BodyHandler.create()); 47 | router.post("/order").produces("application/json").handler(rc -> { 48 | Order o = Json.decodeValue(rc.getBodyAsString(), Order.class); 49 | KafkaProducerRecord record = KafkaProducerRecord.create("orders-out", null, rc.getBodyAsJson(), o.getType().ordinal()); 50 | producer.send(record).onSuccess(recordMetadata -> { 51 | LOGGER.info("Record sent: msg={}, destination={}, partition={}, offset={}", record.value(), recordMetadata.getTopic(), recordMetadata.getPartition(), recordMetadata.getOffset()); 52 | o.setId(recordMetadata.getOffset()); 53 | o.setStatus(OrderStatus.PROCESSING); 54 | rc.response().end(Json.encodePrettily(o)); 55 | }).onFailure(err -> { 56 | Throwable t = err.getCause(); 57 | LOGGER.error("Error sent to topic: {}", t.getMessage()); 58 | o.setStatus(OrderStatus.REJECTED); 59 | }); 60 | }); 61 | vertx.createHttpServer().requestHandler(router).listen(8090); 62 | 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /order-service/src/main/java/pl/piomin/services/vertx/order/model/Order.java: -------------------------------------------------------------------------------- 1 | package pl.piomin.services.vertx.order.model; 2 | 3 | public class Order { 4 | 5 | private Long id; 6 | private OrderType type; 7 | private OrderStatus status; 8 | private int price; 9 | private Long relatedOrderId; 10 | 11 | public Long getId() { 12 | return id; 13 | } 14 | 15 | public void setId(Long id) { 16 | this.id = id; 17 | } 18 | 19 | public OrderType getType() { 20 | return type; 21 | } 22 | 23 | public void setType(OrderType type) { 24 | this.type = type; 25 | } 26 | 27 | public OrderStatus getStatus() { 28 | return status; 29 | } 30 | 31 | public void setStatus(OrderStatus status) { 32 | this.status = status; 33 | } 34 | 35 | public int getPrice() { 36 | return price; 37 | } 38 | 39 | public void setPrice(int price) { 40 | this.price = price; 41 | } 42 | 43 | public Long getRelatedOrderId() { 44 | return relatedOrderId; 45 | } 46 | 47 | public void setRelatedOrderId(Long relatedOrderId) { 48 | this.relatedOrderId = relatedOrderId; 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /order-service/src/main/java/pl/piomin/services/vertx/order/model/OrderStatus.java: -------------------------------------------------------------------------------- 1 | package pl.piomin.services.vertx.order.model; 2 | 3 | public enum OrderStatus { 4 | 5 | NEW, PROCESSING, DONE, REJECTED; 6 | 7 | } 8 | -------------------------------------------------------------------------------- /order-service/src/main/java/pl/piomin/services/vertx/order/model/OrderType.java: -------------------------------------------------------------------------------- 1 | package pl.piomin.services.vertx.order.model; 2 | 3 | public enum OrderType { 4 | 5 | SINGLE, MULTIPLE; 6 | 7 | } 8 | -------------------------------------------------------------------------------- /order-service/src/main/resources/log4j.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | pl.piomin.services 5 | sample-vertx-kafka-messaging 6 | 1.0-SNAPSHOT 7 | pom 8 | 9 | 10 | UTF-8 11 | 21 12 | 4.5.22 13 | piomin_sample-vertx-kafka-messaging 14 | piomin 15 | https://sonarcloud.io 16 | 17 | 18 | 19 | order-service 20 | order-processor 21 | mulpiple-order-processor 22 | all-order-processor 23 | 24 | 25 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "config:base",":dependencyDashboard" 5 | ], 6 | "packageRules": [ 7 | { 8 | "matchUpdateTypes": ["minor", "patch", "pin", "digest"], 9 | "automerge": true 10 | } 11 | ], 12 | "prCreation": "not-pending" 13 | } --------------------------------------------------------------------------------