Initial commit.
This commit is contained in:
33
.gitignore
vendored
Normal file
33
.gitignore
vendored
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
HELP.md
|
||||||
|
target/
|
||||||
|
!.mvn/wrapper/maven-wrapper.jar
|
||||||
|
!**/src/main/**/target/
|
||||||
|
!**/src/test/**/target/
|
||||||
|
|
||||||
|
### STS ###
|
||||||
|
.apt_generated
|
||||||
|
.classpath
|
||||||
|
.factorypath
|
||||||
|
.project
|
||||||
|
.settings
|
||||||
|
.springBeans
|
||||||
|
.sts4-cache
|
||||||
|
|
||||||
|
### IntelliJ IDEA ###
|
||||||
|
.idea
|
||||||
|
*.iws
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
|
||||||
|
### NetBeans ###
|
||||||
|
/nbproject/private/
|
||||||
|
/nbbuild/
|
||||||
|
/dist/
|
||||||
|
/nbdist/
|
||||||
|
/.nb-gradle/
|
||||||
|
build/
|
||||||
|
!**/src/main/**/build/
|
||||||
|
!**/src/test/**/build/
|
||||||
|
|
||||||
|
### VS Code ###
|
||||||
|
.vscode/
|
||||||
15
docker/docker-compose.yml
Normal file
15
docker/docker-compose.yml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
version: '2'
|
||||||
|
services:
|
||||||
|
zookeeper:
|
||||||
|
image: wurstmeister/zookeeper
|
||||||
|
ports:
|
||||||
|
- "2181:2181"
|
||||||
|
kafka:
|
||||||
|
image: wurstmeister/kafka
|
||||||
|
ports:
|
||||||
|
- "9092:9092"
|
||||||
|
environment:
|
||||||
|
KAFKA_ADVERTISED_HOST_NAME: 192.168.0.14
|
||||||
|
KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
|
||||||
|
volumes:
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
73
pom.xml
Normal file
73
pom.xml
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<parent>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-parent</artifactId>
|
||||||
|
<version>2.4.2</version>
|
||||||
|
<relativePath/> <!-- lookup parent from repository -->
|
||||||
|
</parent>
|
||||||
|
<groupId>com.example</groupId>
|
||||||
|
<artifactId>demo</artifactId>
|
||||||
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
|
<name>demo</name>
|
||||||
|
<description>Demo project for Spring Boot</description>
|
||||||
|
<properties>
|
||||||
|
<java.version>11</java.version>
|
||||||
|
</properties>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.kafka</groupId>
|
||||||
|
<artifactId>spring-kafka</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-devtools</artifactId>
|
||||||
|
<scope>runtime</scope>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.kafka</groupId>
|
||||||
|
<artifactId>spring-kafka-test</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<excludes>
|
||||||
|
<exclude>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
</exclude>
|
||||||
|
</excludes>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
</project>
|
||||||
13
src/main/java/com/example/demo/DemoApplication.java
Normal file
13
src/main/java/com/example/demo/DemoApplication.java
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
package com.example.demo;
|
||||||
|
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
|
||||||
|
@SpringBootApplication
|
||||||
|
public class DemoApplication {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
SpringApplication.run(DemoApplication.class, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
package com.example.demo.configuration;
|
||||||
|
|
||||||
|
import com.example.demo.model.User;
|
||||||
|
import org.apache.kafka.clients.consumer.Consumer;
|
||||||
|
import org.apache.kafka.common.errors.TimeoutException;
|
||||||
|
import org.springframework.boot.actuate.health.Health;
|
||||||
|
import org.springframework.boot.actuate.health.HealthIndicator;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.kafka.core.ConsumerFactory;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
public class HealthConfiguration {
|
||||||
|
@Bean
|
||||||
|
public HealthIndicator trackUnitKafkaHealthIndicator(ConsumerFactory<String, User> consumerFactory) {
|
||||||
|
return new KafkaHealthIndicator(consumerFactory);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class KafkaHealthIndicator implements HealthIndicator {
|
||||||
|
private final ConsumerFactory<String, User> consumerFactory;
|
||||||
|
|
||||||
|
public KafkaHealthIndicator(ConsumerFactory<String, User> consumerFactory) {
|
||||||
|
this.consumerFactory = consumerFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Health health() {
|
||||||
|
Health result;
|
||||||
|
|
||||||
|
try (Consumer<String, User> consumer = consumerFactory.createConsumer()) {
|
||||||
|
if (consumer.listTopics().isEmpty()) {
|
||||||
|
result = Health.down()
|
||||||
|
.withDetail("reason", "There is no topic in kafka.")
|
||||||
|
.build();
|
||||||
|
} else {
|
||||||
|
result = Health.up().build();
|
||||||
|
}
|
||||||
|
} catch(TimeoutException ex) {
|
||||||
|
result = Health.down()
|
||||||
|
.withDetail("reason", "Timeout while ping kafka partner")
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
package com.example.demo.configuration;
|
||||||
|
|
||||||
|
import com.example.demo.model.User;
|
||||||
|
import org.apache.kafka.clients.consumer.ConsumerConfig;
|
||||||
|
import org.apache.kafka.clients.producer.ProducerConfig;
|
||||||
|
import org.apache.kafka.common.serialization.StringDeserializer;
|
||||||
|
import org.apache.kafka.common.serialization.StringSerializer;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory;
|
||||||
|
import org.springframework.kafka.core.*;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
public class KafkaConfiguration {
|
||||||
|
private final String bootstrapAddress;
|
||||||
|
private final String usersGroupId;
|
||||||
|
|
||||||
|
public KafkaConfiguration(@Value("${kafka.demo.bootstrapAddress}") String bootstrapAddress,
|
||||||
|
@Value("${kafka.demo.topics.users.groupId}") String usersGroupId) {
|
||||||
|
this.bootstrapAddress = bootstrapAddress;
|
||||||
|
this.usersGroupId = usersGroupId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public ProducerFactory<String, String> userProducerFactory() {
|
||||||
|
final Map<String, Object> props = new HashMap<>();
|
||||||
|
|
||||||
|
props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapAddress);
|
||||||
|
props.put(ProducerConfig.CLIENT_ID_CONFIG, usersGroupId);
|
||||||
|
props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
|
||||||
|
props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
|
||||||
|
props.put(ProducerConfig.METADATA_MAX_AGE_CONFIG, 5000);
|
||||||
|
props.put(ProducerConfig.CONNECTIONS_MAX_IDLE_MS_CONFIG, 5000);
|
||||||
|
props.put(ProducerConfig.MAX_REQUEST_SIZE_CONFIG, 5000);
|
||||||
|
props.put(ProducerConfig.RETRIES_CONFIG, 4); // Retry number regarding partition count
|
||||||
|
props.put(ProducerConfig.REQUEST_TIMEOUT_MS_CONFIG, 5000);
|
||||||
|
props.put("metadata.max.idle.ms", 5000);
|
||||||
|
props.put(ProducerConfig.LINGER_MS_CONFIG, 30);
|
||||||
|
props.put("delivery.timeout.ms", 5120); // request.timeout.ms + linger.ms) * retries
|
||||||
|
|
||||||
|
return new DefaultKafkaProducerFactory<>(props);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public KafkaTemplate<String, String> usersKafkaTemplate() {
|
||||||
|
return new KafkaTemplate<>(userProducerFactory());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public ConsumerFactory<String, User> consumerFactory() {
|
||||||
|
Map<String, Object> props = new HashMap<>();
|
||||||
|
props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapAddress);
|
||||||
|
props.put(ConsumerConfig.GROUP_ID_CONFIG, usersGroupId);
|
||||||
|
props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
|
||||||
|
props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
|
||||||
|
props.put(ConsumerConfig.METADATA_MAX_AGE_CONFIG, 5000);
|
||||||
|
props.put(ConsumerConfig.CONNECTIONS_MAX_IDLE_MS_CONFIG, 5000);
|
||||||
|
props.put(ConsumerConfig.REQUEST_TIMEOUT_MS_CONFIG, 5000);
|
||||||
|
props.put(ConsumerConfig.MAX_POLL_INTERVAL_MS_CONFIG, 5000);
|
||||||
|
props.put(ConsumerConfig.MAX_PARTITION_FETCH_BYTES_CONFIG, 20971520);
|
||||||
|
props.put(ConsumerConfig.DEFAULT_API_TIMEOUT_MS_CONFIG, 2000);
|
||||||
|
return new DefaultKafkaConsumerFactory<>(props);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public ConcurrentKafkaListenerContainerFactory<String, User> kafkaListenerContainerFactory() {
|
||||||
|
ConcurrentKafkaListenerContainerFactory<String, User> factory = new ConcurrentKafkaListenerContainerFactory<>();
|
||||||
|
factory.setConsumerFactory(consumerFactory());
|
||||||
|
return factory;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package com.example.demo.configuration;
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableScheduling
|
||||||
|
public class SpringConfiguration {
|
||||||
|
}
|
||||||
17
src/main/java/com/example/demo/model/User.java
Normal file
17
src/main/java/com/example/demo/model/User.java
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
package com.example.demo.model;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class User {
|
||||||
|
private UUID id;
|
||||||
|
private String name;
|
||||||
|
}
|
||||||
55
src/main/java/com/example/demo/producer/UserProducer.java
Normal file
55
src/main/java/com/example/demo/producer/UserProducer.java
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
package com.example.demo.producer;
|
||||||
|
|
||||||
|
import com.example.demo.model.User;
|
||||||
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.kafka.core.KafkaTemplate;
|
||||||
|
import org.springframework.kafka.support.SendResult;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
@Slf4j
|
||||||
|
public class UserProducer {
|
||||||
|
private final KafkaTemplate<String, String> usersKafkaTemplate;
|
||||||
|
private final ObjectMapper objectMapper;
|
||||||
|
private final String usersTopicName;
|
||||||
|
|
||||||
|
public UserProducer(KafkaTemplate<String, String> usersKafkaTemplate,
|
||||||
|
ObjectMapper objectMapper,
|
||||||
|
@Value("${kafka.demo.topics.users.name}") String usersTopicName) {
|
||||||
|
this.usersKafkaTemplate = usersKafkaTemplate;
|
||||||
|
this.objectMapper = objectMapper;
|
||||||
|
this.usersTopicName = usersTopicName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void send(User user) {
|
||||||
|
serialize(user)
|
||||||
|
.ifPresent(serializedUser -> {
|
||||||
|
log.info("Send user {}", user.getId());
|
||||||
|
usersKafkaTemplate.send(usersTopicName, user.getId().toString(), serializedUser)
|
||||||
|
.completable()
|
||||||
|
.handle((u, throwable) -> {
|
||||||
|
if (throwable != null) {
|
||||||
|
log.error("Error while sending user in kafka :", throwable);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private Optional<String> serialize(User user) {
|
||||||
|
Optional<String> result = Optional.empty();
|
||||||
|
try {
|
||||||
|
result = Optional.of(objectMapper.writeValueAsString(user));
|
||||||
|
} catch (JsonProcessingException e) {
|
||||||
|
log.warn("Unable to serialize as JSON string the User object because it is null.");
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
27
src/main/java/com/example/demo/service/UserService.java
Normal file
27
src/main/java/com/example/demo/service/UserService.java
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
package com.example.demo.service;
|
||||||
|
|
||||||
|
import com.example.demo.model.User;
|
||||||
|
import com.example.demo.producer.UserProducer;
|
||||||
|
import org.springframework.scheduling.annotation.Async;
|
||||||
|
import org.springframework.scheduling.annotation.Scheduled;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class UserService {
|
||||||
|
private final UserProducer userProducer;
|
||||||
|
|
||||||
|
public UserService(UserProducer userProducer) {
|
||||||
|
this.userProducer = userProducer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Async
|
||||||
|
@Scheduled(fixedRate = 2000)
|
||||||
|
public void sendUsersInKafka() {
|
||||||
|
User user = new User();
|
||||||
|
user.setId(UUID.randomUUID());
|
||||||
|
user.setName("User name");
|
||||||
|
userProducer.send(user);
|
||||||
|
}
|
||||||
|
}
|
||||||
25
src/main/resources/application.yml
Normal file
25
src/main/resources/application.yml
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
# These properties are used to configure the actuator healthCheck.
|
||||||
|
management:
|
||||||
|
endpoint:
|
||||||
|
health:
|
||||||
|
show-details: always
|
||||||
|
health:
|
||||||
|
db:
|
||||||
|
enabled: false
|
||||||
|
diskSpace:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
|
||||||
|
kafka:
|
||||||
|
demo:
|
||||||
|
bootstrapAddress: localhost:9092
|
||||||
|
topics:
|
||||||
|
users:
|
||||||
|
name: users-topic
|
||||||
|
groupId: users-groupId
|
||||||
|
|
||||||
|
logging:
|
||||||
|
level:
|
||||||
|
org:
|
||||||
|
apache:
|
||||||
|
# kafka: ERROR
|
||||||
13
src/test/java/com/example/demo/DemoApplicationTests.java
Normal file
13
src/test/java/com/example/demo/DemoApplicationTests.java
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
package com.example.demo;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
|
||||||
|
@SpringBootTest
|
||||||
|
class DemoApplicationTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void contextLoads() {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user