Showing posts with label Spring Boot Framework. Show all posts
Showing posts with label Spring Boot Framework. Show all posts

Sunday, May 16, 2021

Kafka Producer and Consumer Example using Spring Boot

Kafka Producer and Consumer Example using Spring Boot

In this post, we will see how to create a Kafka Producer application which publishes messages to a kafka topic and a Kafka Consumer application which subscribes and consumes messages from the kafka topic.

Pre-requisites for below tutorial:

  • JDK 1.8 or later
  • Maven
  • Git

This example requires below two applications.
  1. Kafka Producer
  2. Kafka Consumer

1. Create Kafka Producer application

Visit the Spring Initializr to generate new project with required dependencies.

Below kafka producer application produces a message to kafka topic every 1 sec.

pom.xml

<?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.5</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>info.code2learn</groupId>
	<artifactId>kafka-producer</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>kafka-producer</name>
	<description>Kafka Producer Example</description>
	<properties>
		<java.version>1.8</java.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</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.springframework.boot</groupId>
			<artifactId>spring-boot-configuration-processor</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>
			</plugin>
		</plugins>
	</build>

</project>



KafkaProducerApplication.java

package info.code2learn.kafkaproducer;

import org.apache.kafka.clients.admin.NewTopic;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.kafka.config.TopicBuilder;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;

@SpringBootApplication
@EnableScheduling
public class KafkaProducerApplication {
	
	private static final Logger log = LoggerFactory.getLogger(KafkaProducerApplication.class);
	private static final String TOPIC = "topic1";
	
	private Integer counter = 0;
	
	@Autowired
	KafkaTemplate<String, String> kafkaTemplate;

	public static void main(String[] args) {
		SpringApplication.run(KafkaProducerApplication.class, args);
	}
	
	@Bean
    public NewTopic topic() {
        return TopicBuilder.name(TOPIC)
                .partitions(10)
                .replicas(1)
                .build();
    }
	
	@Scheduled(fixedDelay = 1000)
	public void produce() {
		String message = "Hello " + counter++;
		log.info("Sending message to topic : {}, message: {}", TOPIC, message);
		kafkaTemplate.send(TOPIC, String.valueOf(message.hashCode()), message);
	}

}




application.yml

server:
  port: 8081
  
spring:
  kafka:
    producer:
      acks: 1
      bootstrap-servers:
      - localhost:9092
      client-id: kafka-producer
      key-serializer:
        org.apache.kafka.common.serialization.StringSerializer
      value-serializer:
        org.apache.kafka.common.serialization.StringSerializer




2. Create Kafka Consumer application

Visit the Spring Initializr to generate new project with required dependencies.
This application consumes the messages from the kafka topic.


pom.xml
    
    <?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.5</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>info.code2learn</groupId>
	<artifactId>kafka-consumer</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>kafka-consumer</name>
	<description>Kafka Consumer Example</description>
	<properties>
		<java.version>1.8</java.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</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.springframework.boot</groupId>
			<artifactId>spring-boot-configuration-processor</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>
			</plugin>
		</plugins>
	</build>

</project> 

KafkaConsumerApplication.java

package info.code2learn.kafkaconsumer;

import org.apache.kafka.clients.admin.NewTopic;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.kafka.annotation.EnableKafka;
import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.kafka.config.TopicBuilder;

@SpringBootApplication
@EnableKafka
public class KafkaConsumerApplication {

	public static void main(String[] args) {
		SpringApplication.run(KafkaConsumerApplication.class, args);
	}
	
	@Bean
    public NewTopic topic() {
        return TopicBuilder.name("topic1")
                .partitions(10)
                .replicas(1)
                .build();
    }
	
	@KafkaListener(id = "kafkaConsumer", topics = { "topic1" })
	public void listen(String message) {
		System.out.println(message);
	}
	
}   
application.yml
    
server:
  port: 8082

spring:
  kafka:
    consumer:
      group-id: kafka-consumer-group
      bootstrap-servers:
      - localhost:9092
      client-id: kafka-consumer
      key-deserializer:
        org.apache.kafka.common.serialization.StringDeserializer
      value-deserializer:
        org.apache.kafka.common.serialization.StringDeserializer
    
    
Start both producer and consumer applications.

Output:



Tuesday, March 30, 2021

Create Spring Boot Application With Centralized Configuration

Create Spring Boot Application With Centralized Configuration

We will see how to create a spring boot application with centralized configuration.

Pre-requisites for below tutorial:

  • JDK 1.8 or later
  • Maven
  • Git

This requires two applications:

  1. a configuration service application (config-server)
  2. a configuration client application (config-client)
1. Create a configuration service application - Config Server

For the service application, visit the Spring Initializr to generate a new project with the required dependency (Config Server).

pom.xml

<?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.4</version>
		<relativePath /> <!-- lookup parent from repository -->
	</parent>
	<groupId>info.code2learn</groupId>
	<artifactId>config-server</artifactId>
	<name>config-server</name>
	<description>Configuration Server</description>

	<properties>
		<java.version>8</java.version>
		<spring-cloud.version>2020.0.2</spring-cloud.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-configuration-processor</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.cloud</groupId>
			<artifactId>spring-cloud-config-server</artifactId>
		</dependency>
	</dependencies>
	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.springframework.cloud</groupId>
				<artifactId>spring-cloud-dependencies</artifactId>
				<version>${spring-cloud.version}</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>



You first need a Config Server to act as a sort of intermediary between your Spring applications and a version-controlled repository of configuration files. You can use Spring Cloud’s @EnableConfigServer to standup a config server that can communicate with other applications. This is a regular Spring Boot application with one annotation added to enable the config server.
ConfigServerApplication.java
  
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.config.server.EnableConfigServer;

@EnableConfigServer
@SpringBootApplication
public class ConfigServerApplication {

	public static void main(String[] args) {
		SpringApplication.run(ConfigServerApplication.class, args);
	}

}
  
  



The Config Server needs to know which repository to manage. There are several choices here, but start with a Git-based file system repository. You could as easily point the Config Server to a Github or GitLab repository.

On the file system, create a new directory and run git init in it. Then add below files in the directory. 

  • config-client.properties
  • config-client-staging.properties
  • config-client-production.properties
Then run git commit in it.

Later, you will connect to the Config Server with a Spring Boot application whose spring.application.name property identifies it as config-client to the Config Server. This is how the Config Server knows which set of configuration to send to a specific client. It also sends all the values from any file named application.properties or application.yml in the Git repository. Property keys in more specifically named files (such as config-client.properties) override those in application.properties or application.yml.

Add a simple property and value with different message related to specific environment (eg.welcome.message = Welcome to config client) to the newly created config-client-<label>.properties files and then  commit the changes using git commit .

config-client.properties

    server.port=8080
    
config-client-staging.properties

    welcome.message=Welcome To Config Client Application - staging
    
config-client-production.properties

    welcome.message=Welcome To Config Client Application - production

Specify the path to the Git repository by specifying the spring.cloud.config.server.git.uri property in configuration-service/src/main/resources/application.properties. You must also specify a different server.port value to avoid port conflicts when you run both this server and another Spring Boot application on the same machine. The following listing (from configuration-service/src/main/resources/application.properties) shows such an application.properties file:


server.port=8888
spring.cloud.config.server.git.uri=${HOME}/Desktop/config-files

2. Create a configuration client application - Config Client
Now we need to create a client application which loads property files from config server. We need to add the org.springframework.cloud:spring-cloud-starter-config  dependency, to connect to the Config Server.

pom.xml

<?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.4</version>
		<relativePath /> <!-- lookup parent from repository -->
	</parent>
	<groupId>info.code2learn</groupId>
	<artifactId>config-client</artifactId>
	<name>config-client</name>
	<description>Configuration Client</description>
	
	<properties>
		<java.version>8</java.version>
		<spring-cloud.version>2020.0.2</spring-cloud.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.cloud</groupId>
			<artifactId>spring-cloud-starter-config</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.springframework.cloud</groupId>
				<artifactId>spring-cloud-dependencies</artifactId>
				<version>${spring-cloud.version}</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>


Specify the client’s  spring.application.nameas  config-clientand the location of the Config Server spring.config.importin config-client/src/main/resources/application.properties.
config-client/src/main/resources/application.properties
spring.application.name=config-client
spring.config.import=optional:configserver:http://localhost:8888/
management.endpoints.web.exposure.include=*

The client can access any value in the Config Server by using the traditional mechanisms (such as @ConfigurationProperties or @Value("${…​}") or through the Environment abstraction).

Create a rest controller that returns the resolved message property’s value.

ConfigClientApplication.java

package info.code2learn.configclient;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.context.config.annotation.RefreshScope;

@RefreshScope
@SpringBootApplication
public class ConfigClientApplication {

	public static void main(String[] args) {
		SpringApplication.run(ConfigClientApplication.class, args);
	}

}

ConfigClientController.java

package info.code2learn.configclient.controller;

import org.springframework.beans.factory.annotatioTest the Applicationn.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class ConfigClientController {
	
	@Value("${welcome.message}")
	private String message;
	
	@RequestMapping("/message")
	public ResponseEntity<String> message(String name) {
		return new ResponseEntity<String>(message, HttpStatus.OK);
	}
	
}

Test the application

Start the Config Server first and then, once it is running, start the client using profile 'staging' or 'production' (eg. mvn spring-boot:run -Dspring-boot.run.profiles=staging). Open the client app in the browser using url http://localhost:8080/message . There, you should see Welcome message in the response specific to the profile.






You can also see the configuration details for a particular client application using  http://localhost:8888/config-client/staging


Click here for github link

Spring Boot HTTPS Rest Call with Rest Template

We will see how to call an application which is HTTPS enabled using Spring Boot RestTemplate in a client application. Prerequisites:  1) M...