├── .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 | --------------------------------------------------------------------------------