add spring-rabbitmq example
Signed-off-by: Mohammad Sadegh Sheikh Zahedi <sheikhoo.iran@gmail.com>
This commit is contained in:
parent
3746d65ad1
commit
b670c71c4a
29
spring-rabbitmq/.docker/docker-compose.yaml
Normal file
29
spring-rabbitmq/.docker/docker-compose.yaml
Normal file
@ -0,0 +1,29 @@
|
||||
services:
|
||||
backend:
|
||||
build: backend
|
||||
ports:
|
||||
- 8080:8080
|
||||
environment:
|
||||
- RABBITMQ_HOST=rabbitmq
|
||||
- RABBITMQ_QUEUE_NAME=springboot-queue
|
||||
- RABBITMQ_FANOUT_EXCHANGE=springboot-exchange
|
||||
networks:
|
||||
- spring-rabbitmq
|
||||
depends_on:
|
||||
- rabbitmq
|
||||
rabbitmq:
|
||||
image: rabbitmq
|
||||
restart: always
|
||||
volumes:
|
||||
- rabbitmq-data:/var/lib/rabbitmq
|
||||
networks:
|
||||
- spring-rabbitmq
|
||||
environment:
|
||||
- RABBITMQ_DEFAULT_USER=guest
|
||||
- RABBITMQ_DEFAULT_PASS=guest
|
||||
expose:
|
||||
- 5672
|
||||
volumes:
|
||||
rabbitmq-data:
|
||||
networks:
|
||||
spring-rabbitmq:
|
60
spring-rabbitmq/README.md
Normal file
60
spring-rabbitmq/README.md
Normal file
@ -0,0 +1,60 @@
|
||||
## Compose sample application
|
||||
|
||||
### Java application with Spring framework and Rabbitmq
|
||||
|
||||
Project structure:
|
||||
```
|
||||
.
|
||||
├── backend
|
||||
│ ├── Dockerfile
|
||||
│ └── ...
|
||||
├── compose.yaml
|
||||
└── README.md
|
||||
|
||||
```
|
||||
|
||||
[_compose.yaml_](compose.yaml)
|
||||
```
|
||||
services:
|
||||
backend:
|
||||
build: backend
|
||||
ports:
|
||||
- 8080:8080
|
||||
rabbitmq:
|
||||
rabbitmq:3.9-management
|
||||
...
|
||||
```
|
||||
The compose file defines an application with two services `backend` and `rabbitmq`.
|
||||
When deploying the application, docker compose maps port 8080 of the backend service container to port 8080 of the host as specified in the file.
|
||||
Make sure port 8080 on the host is not already being in use.
|
||||
|
||||
## Deploy with docker compose
|
||||
|
||||
```
|
||||
$ docker compose up -d
|
||||
```
|
||||
|
||||
## Expected result
|
||||
|
||||
Listing containers must show two containers running and the port mapping as below:
|
||||
```
|
||||
$ docker ps
|
||||
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
|
||||
86f6bb2a1585 spring-rabbitmq-backend "java -cp app:app/li…" 6 minutes ago Up 6 minutes 0.0.0.0:8080->8080/tcp spring-rabbitmq-backend-1
|
||||
0c4522d3f075 rabbitmq "docker-entrypoint.s…" 6 minutes ago Up 6 minutes 4369/tcp, 5671-5672/tcp, 15691-15692/tcp, 25672/tcp spring-rabbitmq-rabbitmq-1
|
||||
```
|
||||
|
||||
After the application starts, you can send and receive data to rabbitmq with this api :
|
||||
|
||||
`POST`: http://localhost:8080/sendMessage
|
||||
Body:
|
||||
```
|
||||
{
|
||||
"title":""Hello, rabbitmq",
|
||||
"text":"This is my test messag",
|
||||
"sender":"admin"
|
||||
}
|
||||
```
|
||||
|
||||
`GET`: http://localhost:8080/getMessage
|
||||
|
39
spring-rabbitmq/backend/Dockerfile
Normal file
39
spring-rabbitmq/backend/Dockerfile
Normal file
@ -0,0 +1,39 @@
|
||||
# syntax=docker/dockerfile:1.4
|
||||
|
||||
FROM --platform=$BUILDPLATFORM maven:3.8.5-eclipse-temurin-17 AS builder
|
||||
WORKDIR /workdir/server
|
||||
COPY pom.xml /workdir/server/pom.xml
|
||||
RUN mvn dependency:go-offline
|
||||
|
||||
COPY src /workdir/server/src
|
||||
RUN mvn install
|
||||
|
||||
FROM builder AS dev-envs
|
||||
RUN <<EOF
|
||||
apt-get update
|
||||
apt-get install -y --no-install-recommends git
|
||||
EOF
|
||||
|
||||
RUN <<EOF
|
||||
useradd -s /bin/bash -m vscode
|
||||
groupadd docker
|
||||
usermod -aG docker vscode
|
||||
EOF
|
||||
# install Docker tools (cli, buildx, compose)
|
||||
COPY --from=gloursdocker/docker / /
|
||||
CMD ["mvn", "spring-boot:run"]
|
||||
|
||||
FROM builder as prepare-production
|
||||
RUN mkdir -p target/dependency
|
||||
WORKDIR /workdir/server/target/dependency
|
||||
RUN jar -xf ../*.jar
|
||||
|
||||
FROM eclipse-temurin:17-jre-focal
|
||||
|
||||
EXPOSE 8080
|
||||
VOLUME /tmp
|
||||
ARG DEPENDENCY=/workdir/server/target/dependency
|
||||
COPY --from=prepare-production ${DEPENDENCY}/BOOT-INF/lib /app/lib
|
||||
COPY --from=prepare-production ${DEPENDENCY}/META-INF /app/META-INF
|
||||
COPY --from=prepare-production ${DEPENDENCY}/BOOT-INF/classes /app
|
||||
ENTRYPOINT ["java","-cp","app:app/lib/*","com.company.project.Application"]
|
50
spring-rabbitmq/backend/pom.xml
Normal file
50
spring-rabbitmq/backend/pom.xml
Normal file
@ -0,0 +1,50 @@
|
||||
<?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>3.0.1</version>
|
||||
<relativePath/> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<groupId>com.company</groupId>
|
||||
<artifactId>spring-rabbitmq</artifactId>
|
||||
<version>0.0.1</version>
|
||||
<name>spring-rabbitmq</name>
|
||||
<description>Demo project for Spring Boot and Rabbitmq</description>
|
||||
<properties>
|
||||
<java.version>17</java.version>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-amqp</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.amqp</groupId>
|
||||
<artifactId>spring-rabbit-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>
|
@ -0,0 +1,12 @@
|
||||
package com.company.project;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class Application {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(Application.class, args);
|
||||
}
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
package com.company.project.config;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.company.project.model.Message;
|
||||
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class QueueConsumer {
|
||||
|
||||
private final RabbitTemplate rabbitTemplate;
|
||||
|
||||
@Value("${queue.name}")
|
||||
private String queueName;
|
||||
|
||||
@Autowired
|
||||
public QueueConsumer(RabbitTemplate rabbitTemplate) {
|
||||
this.rabbitTemplate = rabbitTemplate;
|
||||
}
|
||||
|
||||
private String receiveMessage() {
|
||||
String message = (String) rabbitTemplate.receiveAndConvert(queueName);
|
||||
return message;
|
||||
}
|
||||
|
||||
public Message processMessage() throws JsonProcessingException {
|
||||
String message = receiveMessage();
|
||||
return new ObjectMapper().readValue(message, Message.class);
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
package com.company.project.config;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.company.project.model.Message;
|
||||
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class QueueProducer {
|
||||
|
||||
@Value("${fanout.exchange}")
|
||||
private String fanoutExchange;
|
||||
|
||||
private final RabbitTemplate rabbitTemplate;
|
||||
|
||||
@Autowired
|
||||
public QueueProducer(RabbitTemplate rabbitTemplate) {
|
||||
this.rabbitTemplate = rabbitTemplate;
|
||||
}
|
||||
|
||||
public void produce(Message message) throws JsonProcessingException {
|
||||
|
||||
rabbitTemplate.setExchange(fanoutExchange);
|
||||
rabbitTemplate.convertAndSend(new ObjectMapper().writeValueAsString(message));
|
||||
}
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
package com.company.project.config;
|
||||
|
||||
import org.springframework.amqp.core.Binding;
|
||||
import org.springframework.amqp.core.BindingBuilder;
|
||||
import org.springframework.amqp.core.FanoutExchange;
|
||||
import org.springframework.amqp.core.Queue;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
public class RabbitConfiguration {
|
||||
@Value("${fanout.exchange}")
|
||||
private String fanoutExchange;
|
||||
|
||||
@Value("${queue.name}")
|
||||
private String queueName;
|
||||
|
||||
@Bean
|
||||
Queue queue() {
|
||||
return new Queue(queueName, true);
|
||||
}
|
||||
|
||||
@Bean
|
||||
FanoutExchange fanoutExchange() {
|
||||
return new FanoutExchange(fanoutExchange);
|
||||
}
|
||||
|
||||
@Bean
|
||||
Binding binding(Queue queue, FanoutExchange fanoutExchange) {
|
||||
return BindingBuilder.bind(queue).to(fanoutExchange);
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
package com.company.project.controller;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.company.project.config.QueueConsumer;
|
||||
import com.company.project.config.QueueProducer;
|
||||
import com.company.project.model.Message;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
@Controller
|
||||
@RequestMapping("/")
|
||||
public class MessageController {
|
||||
|
||||
private final QueueProducer queueProducer;
|
||||
private final QueueConsumer queueConsumer;
|
||||
|
||||
public MessageController(QueueProducer queueProducer, QueueConsumer queueConsumer) {
|
||||
this.queueProducer = queueProducer;
|
||||
this.queueConsumer = queueConsumer;
|
||||
}
|
||||
|
||||
@GetMapping("getMessage")
|
||||
public ResponseEntity<?> getMessage() throws JsonProcessingException {
|
||||
Message message = queueConsumer.processMessage();
|
||||
return new ResponseEntity<Message>(message, HttpStatus.OK);
|
||||
}
|
||||
|
||||
@PostMapping("sendMessage")
|
||||
public ResponseEntity<?> sendMessage(@RequestBody Message message) throws JsonProcessingException {
|
||||
queueProducer.produce(message);
|
||||
return new ResponseEntity<Message>(HttpStatus.CREATED);
|
||||
}
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
package com.company.project.model;
|
||||
|
||||
public class Message {
|
||||
|
||||
private String title;
|
||||
private String text;
|
||||
private String sender;
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public String getText() {
|
||||
return text;
|
||||
}
|
||||
|
||||
public void setText(String text) {
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
public String getSender() {
|
||||
return sender;
|
||||
}
|
||||
|
||||
public void setSender(String sender) {
|
||||
this.sender = sender;
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
server.port= 8080
|
||||
spring.main.banner-mode= off
|
||||
spring.rabbitmq.host= ${RABBITMQ_HOST:localhost}
|
||||
spring.rabbitmq.port= 5672
|
||||
spring.rabbitmq.username= guest
|
||||
spring.rabbitmq.password= guest
|
||||
queue.name= ${RABBITMQ_QUEUE_NAME:springboot-queue}
|
||||
fanout.exchange= ${RABBITMQ_FANOUT_EXCHANGE:springboot-exchange}
|
||||
|
29
spring-rabbitmq/compose.yaml
Normal file
29
spring-rabbitmq/compose.yaml
Normal file
@ -0,0 +1,29 @@
|
||||
services:
|
||||
backend:
|
||||
build: backend
|
||||
ports:
|
||||
- 8080:8080
|
||||
environment:
|
||||
- RABBITMQ_HOST=rabbitmq
|
||||
- RABBITMQ_QUEUE_NAME=springboot-queue
|
||||
- RABBITMQ_FANOUT_EXCHANGE=springboot-exchange
|
||||
networks:
|
||||
- spring-rabbitmq
|
||||
depends_on:
|
||||
- rabbitmq
|
||||
rabbitmq:
|
||||
image: rabbitmq
|
||||
restart: always
|
||||
volumes:
|
||||
- rabbitmq-data:/var/lib/rabbitmq
|
||||
networks:
|
||||
- spring-rabbitmq
|
||||
environment:
|
||||
- RABBITMQ_DEFAULT_USER=guest
|
||||
- RABBITMQ_DEFAULT_PASS=guest
|
||||
expose:
|
||||
- 5672
|
||||
volumes:
|
||||
rabbitmq-data:
|
||||
networks:
|
||||
spring-rabbitmq:
|
Loading…
Reference in New Issue
Block a user