SpringBoot AMQP with RabbitMQ in Kubernetes

We write a spring boot based message sender and receiver with rabbitMQ within kubernetes (k8s).

SpringBoot AMQP with RabbitMQ in Kubernetes

We write a spring boot based message sender and receiver with rabbitMQ within kubernetes (k8s).

As with previous blogs we use a docker desktop kubernetes local infrastructure and helm to install the capability.

Use helm to install rabbitmq onto K8s.

helm install --name rab --set auth.username=admin,auth.password=admin,metrics.enabled=true stable/rabbitmq

Fix the password to this (note: rabbitmq does not like short passwords, it will accept them in this setting but you won't be able to login).

helm upgrade --set  auth.username=admin,auth.password=secretpassword,metrics.enabled=true  rab bitnami/rabbitmq

Otherwise the default username is user and the password is obtained as follows:

echo "Password      : $(kubectl get secret rab-rabbitmq -o jsonpath="{.data.rabbitmq-password}" | base64 --decode)"
 echo "ErLang Cookie : $(kubectl get secret rab-rabbitmq -o jsonpath="{.data.rabbitmq-erlang-cookie}" | base64 --decode)"

RabbitMQ can be accessed within the cluster on

rab-rabbitmq.default.svc.cluster.local

To access from outside the cluster, perform the following steps:

To access the RabbitMQ AMQP port:

kubectl port-forward  svc/rab-rabbitmq 5672:5672

   echo "URL : amqp://127.0.0.1:5672/"

To access the RabbitMQ management interface:

kubectl port-forward svc/rab-rabbitmq 15672:15672

http://127.0.0.1:15672/#/

Under the admin tab, create user and password guest/guest,  which is the default for spring rabbitmq. There are property settings to change this (or use enviroment variables and set this in K8s, to be picked up by spring)

Spring AMPQ code

Spring initialzr

https://start.spring.io/#!type=maven-project&language=java&platformVersion=2.3.1.RELEASE&packaging=jar&jvmVersion=1.8&groupId=uk.co.actualcode&artifactId=vadal-mq&name=vadal-mq&description=Demo%20project%20for%20Spring%20Boot&packageName=uk.co.actualcode.vadal-mq&dependencies=amqp,web

Pom needs the following:

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-amqp</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-rest</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.amqp</groupId>
			<artifactId>spring-rabbit-test</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>io.micrometer</groupId>
			<artifactId>micrometer-registry-prometheus</artifactId>
			<scope>runtime</scope>
		</dependency>
	</dependencies>

Prometheus has been added so we can monitor the service via grafana later (see this blog).

Application.yml

spring.rabbitmq.host: rab-rabbitmq.default.svc.cluster.local

server.port: 7777

management:
  endpoints:
    web:
      exposure:
        include: "*"

Vadal-mq-sender Application

@SpringBootApplication
@RestController
@Slf4j
public class VadalMqApplication {

    public static final String VADALQ = "vadalQ";

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

    @Autowired
    RabbitTemplate rabbitTemplate;

    @Bean
    public Queue vadalQueue() {
      return new Queue(VADALQ, false);
    }

    @GetMapping("/m/{m}")
    public void send(@PathVariable String m) {
      rabbitTemplate.convertAndSend(VADALQ, m);
    }
}

Create the image

mvn spring-boot:build-image

Deploy the image

kubectl create deployment vadal-mq-sender --image=vadal-mq-sender:0.0.1-SNAPSHOT

deployment.apps/vadal-mq-sender created

Expose the Internal Port 7777

kubectl expose deployment vadal-mq-sender --type NodePort --port 8889 --target-port 7777

If you've got an existing image update it like so:

kubectl patch deploy vadal-mq-receiver -p '{"spec":{"template":{"spec":{"terminationGracePeriodSeconds":31}}}}'

kubectl get svc to find the port.

kubectl get svc vadal-mq-sender
NAME              TYPE       CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE
vadal-mq-sender   NodePort   10.108.231.116           8889:31498/TCP   3d

sender endpoint port is 31498, send a message hi

http://localhost:31498/m/hi

Now for the receiver. This will receive messages and store them in a database so that they can be retrieved on demand.

Spring initialzr

https://start.spring.io/#!type=maven-project&language=java&platformVersion=2.3.1.RELEASE&packaging=jar&jvmVersion=1.8&groupId=uk.co.actualcode&artifactId=vadal-mq-receiver&name=vadal-mq-receiver&description=Demo%20project%20for%20Spring%20Boot&packageName=uk.co.actualcode.vadal-mq-receiver&dependencies=amqp,data-rest,h2,prometheus

Pom is similar

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-amqp</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-rest</artifactId>
		</dependency>

		<dependency>
			<groupId>com.h2database</groupId>
			<artifactId>h2</artifactId>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>io.micrometer</groupId>
			<artifactId>micrometer-registry-prometheus</artifactId>
			<scope>runtime</scope>
		</dependency>
	</dependencies>

We are also making use of spring data rest and the in memory H2 database.

Application is similar too:

spring:
  application:
    name: vadalmqreceiver
  rabbitmq.host: rab-rabbitmq.default.svc.cluster.local

server.port: 7777

management:
  endpoints:
    web:
      exposure:
        include: "*"

The application is as follows:

@SpringBootApplication
@RestController
@Slf4j
public class VadalMqReceiverApplication {

	public static final String VADALQ = "vadalQ";

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

	@Autowired
	public MsgRepo msgRepo;

	@Bean
	public Queue vadalQueue() {
	    return new Queue(VADALQ, false);
	}

	@RabbitListener(queues = VADALQ)
	public void listen(String in) {
	    log.info("Message read from vadalQ : " + in);
	    msgRepo.save(new Msg(in));
	}

	@GetMapping("/put/{p}")
	public String put(@PathVariable String p) {
	    Msg save = msgRepo.save(new Msg(p));
	    return save.toString();
	}

	@GetMapping("/get")
	public Iterable<Msg> get() {
	    Iterable<Msg> all = msgRepo.findAll();
	    msgRepo.deleteAll();
	    return all;
	}

}

This listens on a message queue and saves any received message to the database. The messages can be retrieved uses the /get endpoint.

Create the image.

mvn spring-boot:build-image

Deploy it and expose the port

kubectl create deployment vadal-mq-receiver --image=vadal-mq-receiver:0.0.1-SNAPSHOT
kubectl expose deployment vadal-mq-receiver --type NodePort --port 8899 --target-port 7777

Check the port

kubectl get svc vadal-mq-receiver
NAME                TYPE       CLUSTER-IP     EXTERNAL-IP   PORT(S)          AGE
vadal-mq-receiver   NodePort   10.96.60.154           8899:30676/TCP   10d

http://localhost:30676/get

[
{
"id": 3,
"m": "hi"
},
{
"id": 4,
"m": "this is"
},
{
"id": 5,
"m": "a rabbitmq"
},
{
"id": 6,
"m": "message"
}
]

The rabbitmq management UI provides a view of the queue traffic.

Conclusion

We installed RabbitMQ on a docker desktop kubernetes local infrastructure. We created a sender and a receiver service using spring boot starter dependencies and we were able to publish to and listen on to the rabbit mq broker running within k8s.

The code for this blog can be found at

https://gitlab.com/lightphos/spring/vadal

Related Article