├── .gitignore
├── README.md
├── consumer
├── pom.xml
└── src
│ └── main
│ ├── java
│ └── br
│ │ └── com
│ │ └── emmanuelneri
│ │ └── consumer
│ │ ├── ConsumerAppConfig.java
│ │ ├── OrderConsumer.java
│ │ └── model
│ │ └── Order.java
│ └── resources
│ └── application.properties
├── docker-compose.yml
├── pom.xml
├── producer
├── pom.xml
└── src
│ └── main
│ ├── java
│ └── br
│ │ └── com
│ │ └── emmanuelneri
│ │ └── producer
│ │ ├── OrderProducer.java
│ │ ├── ProducerAppConfig.java
│ │ └── controller
│ │ └── OrderController.java
│ └── resources
│ └── application.properties
└── send-order.sh
/.gitignore:
--------------------------------------------------------------------------------
1 | target/
2 | !.mvn/wrapper/maven-wrapper.jar
3 |
4 | ### STS ###
5 | .apt_generated
6 | .classpath
7 | .factorypath
8 | .project
9 | .settings
10 | .springBeans
11 | .sts4-cache
12 |
13 | ### IntelliJ IDEA ###
14 | .idea
15 | *.iws
16 | *.iml
17 | *.ipr
18 |
19 | ### NetBeans ###
20 | nbproject/private/
21 | build/
22 | nbbuild/
23 | dist/
24 | nbdist/
25 | .nb-gradle/
26 | .mvn/
27 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # spring-boot-kafka
2 |
3 | ## Pré requisitos
4 |
5 | - Maven 3+
6 | - Java 8+
7 | - Docker 18.02.0+
8 |
9 | ## Preparando ambiente
10 |
11 | - Execute o `docker-compose up` para inicializar o Zookeeper e Kafka.
12 | - Execute `mvn clean package` na pasta do projeto para realizar o build das aplicações.
13 |
14 | ## Executando
15 |
16 | - Inicialize o projeto `producer`
17 | ````
18 | cd producer
19 | mvn spring-boot:run
20 | ````
21 |
22 | Obs: a aplicação Producer disponibiliza o endpoint `POST http://localhost:8080/orders` para receber os eventos dos pedidos.
23 |
24 |
25 | - Inicialize o projeto `consumer`
26 | ````
27 | cd consumer
28 | mvn spring-boot:run
29 | ````
30 | Obs: O projeto do consumer não tem endpoint, ele apenas conecta no tópico do Kafka para escutar o stream.
31 |
32 |
33 | ## Executando
34 |
35 |
36 | Para testar, pode ser utilizado o seguinte comando: `./send-order.sh "{\"identifier\": \"12343\",\"customer\": \"Customer X\", \"value\": 1500}"`, onde será inserido o pedido no tópico do Kafka, via a aplicação `producer`, e será cosumido pela aplicação `consumer`, como no log abaixo:
37 | ````
38 | 2019-05-13 19:41:45.033 INFO 2103 --- [ntainer#0-0-C-1] b.c.emmanuelneri.consumer.OrderConsumer : Order: Order(identifier=12343, customer=Customer X, value=1500)
39 | ````
40 |
--------------------------------------------------------------------------------
/consumer/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 |
8 | spring-boot-kafka
9 | br.com.emmanuelneri
10 | 0.0.1-SNAPSHOT
11 |
12 |
13 | consumer
14 |
15 |
16 |
17 | org.springframework.boot
18 | spring-boot-starter
19 |
20 |
21 | org.springframework.kafka
22 | spring-kafka
23 | ${spring-kafka.version}
24 |
25 |
26 | com.fasterxml.jackson.core
27 | jackson-databind
28 | ${jackson.version}
29 |
30 |
31 |
32 |
33 |
34 |
35 | org.springframework.boot
36 | spring-boot-maven-plugin
37 |
38 |
39 | com.spotify
40 | dockerfile-maven-plugin
41 | ${docker.spotify.plugin.version}
42 |
43 | ${docker.repository}/${project.artifactId}
44 |
45 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/consumer/src/main/java/br/com/emmanuelneri/consumer/ConsumerAppConfig.java:
--------------------------------------------------------------------------------
1 | package br.com.emmanuelneri.consumer;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 |
6 | @SpringBootApplication
7 | public class ConsumerAppConfig {
8 |
9 | public static void main(String[] args) {
10 | SpringApplication.run(ConsumerAppConfig.class, args);
11 | }
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/consumer/src/main/java/br/com/emmanuelneri/consumer/OrderConsumer.java:
--------------------------------------------------------------------------------
1 | package br.com.emmanuelneri.consumer;
2 |
3 | import br.com.emmanuelneri.consumer.model.Order;
4 | import lombok.extern.slf4j.Slf4j;
5 | import org.apache.kafka.clients.consumer.ConsumerRecord;
6 | import org.springframework.kafka.annotation.KafkaListener;
7 | import org.springframework.stereotype.Component;
8 |
9 | @Component
10 | @Slf4j
11 | public class OrderConsumer {
12 |
13 | @KafkaListener(topics = "${order.topic}", groupId = "${spring.kafka.consumer.group-id}")
14 | public void consumer(final ConsumerRecord consumerRecord) {
15 | log.info("key: " + consumerRecord.key());
16 | log.info("Headers: " + consumerRecord.headers());
17 | log.info("Partion: " + consumerRecord.partition());
18 | log.info("Order: " + consumerRecord.value());
19 | }
20 | }
--------------------------------------------------------------------------------
/consumer/src/main/java/br/com/emmanuelneri/consumer/model/Order.java:
--------------------------------------------------------------------------------
1 | package br.com.emmanuelneri.consumer.model;
2 |
3 | import lombok.Data;
4 |
5 | import java.io.Serializable;
6 | import java.math.BigDecimal;
7 |
8 | @Data
9 | public class Order implements Serializable {
10 |
11 | private String identifier;
12 | private String customer;
13 | private BigDecimal value;
14 | }
15 |
--------------------------------------------------------------------------------
/consumer/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 | spring.kafka.consumer.group-id=order-consumer-group
2 | spring.kafka.consumer.auto-offset-reset=earliest
3 | spring.kafka.consumer.key-deserializer=org.apache.kafka.common.serialization.StringDeserializer
4 | spring.kafka.consumer.value-deserializer=org.springframework.kafka.support.serializer.JsonDeserializer
5 |
6 | order.topic=ordertopic
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '3.6'
2 | services:
3 | zookeeper:
4 | image: confluentinc/cp-zookeeper:5.2.1
5 | environment:
6 | ZOOKEEPER_CLIENT_PORT: 2181
7 | ZOOKEEPER_TICK_TIME: 2000
8 | ZOOKEEPER_SYNC_LIMIT: 2
9 |
10 | kafka:
11 | image: confluentinc/cp-kafka:5.2.1
12 | ports:
13 | - "9092:9092"
14 | depends_on:
15 | - zookeeper
16 | environment:
17 | KAFKA_BROKER_ID: 1
18 | KAFKA_ZOOKEEPER_CONNECT: 'zookeeper:2181'
19 | KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://localhost:9092
20 | KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
21 | KAFKA_AUTO_CREATE_TOPICS_ENABLE: "true"
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 | org.springframework.boot
7 | spring-boot-starter-parent
8 | 2.1.4.RELEASE
9 |
10 |
11 |
12 | br.com.emmanuelneri
13 | spring-boot-kafka
14 | 0.0.1-SNAPSHOT
15 | pom
16 |
17 | spring-boot-kafka
18 | Spring Boot Kafka
19 |
20 |
21 | UTF-8
22 | UTF-8
23 | 1.8
24 |
25 | 2.2.2.RELEASE
26 | 1.16.10
27 | 2.9.8
28 |
29 | 1.4.10
30 | spring-boot-kafka
31 |
32 |
33 |
34 |
35 | org.projectlombok
36 | lombok
37 | ${lombok.version}
38 |
39 |
40 |
41 |
42 | producer
43 | consumer
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/producer/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 |
8 | spring-boot-kafka
9 | br.com.emmanuelneri
10 | 0.0.1-SNAPSHOT
11 |
12 |
13 | producer
14 |
15 |
16 |
17 | org.springframework.boot
18 | spring-boot-starter-web
19 |
20 |
21 | org.springframework.kafka
22 | spring-kafka
23 | ${spring-kafka.version}
24 |
25 |
26 |
27 |
28 |
29 |
30 | org.springframework.boot
31 | spring-boot-maven-plugin
32 |
33 |
34 | com.spotify
35 | dockerfile-maven-plugin
36 | ${docker.spotify.plugin.version}
37 |
38 | ${docker.repository}/${project.artifactId}
39 |
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/producer/src/main/java/br/com/emmanuelneri/producer/OrderProducer.java:
--------------------------------------------------------------------------------
1 | package br.com.emmanuelneri.producer;
2 |
3 | import org.springframework.beans.factory.annotation.Value;
4 | import org.springframework.kafka.core.KafkaTemplate;
5 | import org.springframework.stereotype.Component;
6 | import org.springframework.web.bind.annotation.RequestBody;
7 |
8 | import java.util.UUID;
9 |
10 | @Component
11 | public class OrderProducer {
12 |
13 | @Value("${order.topic}")
14 | private String orderTopic;
15 |
16 | private final KafkaTemplate kafkaTemplate;
17 |
18 | public OrderProducer(final KafkaTemplate kafkaTemplate) {
19 | this.kafkaTemplate = kafkaTemplate;
20 | }
21 |
22 | public void send(final @RequestBody String order) {
23 | final String mensageKey = UUID.randomUUID().toString();
24 | kafkaTemplate.send(orderTopic, mensageKey, order);
25 | }
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/producer/src/main/java/br/com/emmanuelneri/producer/ProducerAppConfig.java:
--------------------------------------------------------------------------------
1 | package br.com.emmanuelneri.producer;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 |
6 | @SpringBootApplication
7 | public class ProducerAppConfig {
8 |
9 | public static void main(String[] args) {
10 | SpringApplication.run(ProducerAppConfig.class, args);
11 | }
12 |
13 | }
--------------------------------------------------------------------------------
/producer/src/main/java/br/com/emmanuelneri/producer/controller/OrderController.java:
--------------------------------------------------------------------------------
1 | package br.com.emmanuelneri.producer.controller;
2 |
3 | import br.com.emmanuelneri.producer.OrderProducer;
4 | import lombok.extern.slf4j.Slf4j;
5 | import org.springframework.web.bind.annotation.RequestBody;
6 | import org.springframework.web.bind.annotation.RequestMapping;
7 | import org.springframework.web.bind.annotation.RequestMethod;
8 | import org.springframework.web.bind.annotation.RestController;
9 |
10 | @RestController
11 | @RequestMapping(value = "/orders")
12 | @Slf4j
13 | public class OrderController {
14 |
15 | private final OrderProducer orderProducer;
16 |
17 | public OrderController(OrderProducer orderProducer) {
18 | this.orderProducer = orderProducer;
19 | }
20 |
21 | @RequestMapping(method = RequestMethod.POST)
22 | public void send(@RequestBody String order) {
23 | orderProducer.send(order);
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/producer/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 | server.port=8080
2 |
3 | spring.kafka.producer.bootstrap-servers=localhost:9092
4 | spring.kafka.producer.key-serializer=org.apache.kafka.common.serialization.StringSerializer
5 | spring.kafka.producer.value-serializer=org.springframework.kafka.support.serializer.JsonSerializer
6 | spring.kafka.consumer.properties.spring.json.add.type.headers=false
7 |
8 | order.topic=ordertopic
9 |
--------------------------------------------------------------------------------
/send-order.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | data=$1
4 | url=http://localhost:8080/orders
5 |
6 | curl -d "$data" -H "Content-Type: application/json" -X POST ${url}
7 |
--------------------------------------------------------------------------------