This commit is contained in:
Mohammad Sadegh Sheikh Zahedi 2023-01-10 08:46:18 +08:00 committed by GitHub
commit bbb945ab97
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 393 additions and 0 deletions

View 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:

62
spring-rabbitmq/README.md Normal file
View File

@ -0,0 +1,62 @@
## 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:
image: 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

View 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"]

View 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>

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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));
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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}

View 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: