1. Introduction
Google Cloud Pub/Sub might be a great tool to integrate decoupled systems hosted on GCP as well as elsewhere on the Internet. In this step-by-step guide, we will learn how to set up Google Cloud Pub/Sub with Spring Boot Java application.
Moreover, I will show you how to configure it for serialization and deserialization of POJOs using Jackson JSON.
2. Setup Google Cloud Project
2.1. Create Topic and Subscription
As the first step, let’s go to the GCP console, and let’s enable the Pub/Sub API for our future Spring Boot project by clicking the Enable button.
Alternatively, we can do the same with a gcloud CLI command:
gcloud services enable pubsub.googleapis.com
As the next step, let’s go to the Topics tab in the left sidebar and create a new topic named example:
Or again, with a gcloud command:
gcloud pubsub topics create example
Nextly, let’s go to the Subscriptions tab and click the create subscription button.
On this page, we can specify the subscription ID. Let’s call it an example-subscription and choose our previously created topic:
Or, as usual, it can be done using CLI command:
gcloud pubsub subscriptions create example-subscription --topic=example
If we would like to validate whether everything has been created, we can check it with these two commands:
gcloud pubsub subscriptions describe example-subscription gcloud pubsub topics describe example
2.2. Setup Service Account
In this paragraph, we will learn how to create a service account, which will be used later in our Spring Boot project.
As the first step, let’s go to the GCP IAM & Admin console and select the Service Accounts tab. Then, let’s click the create service account button and specify the name as follows:
Nextly, we need to click continue, and on the next page, let’s add Pub/Sub Admin role:
After the role has been added, we need to click next and finally click done. We will be redirected to the page showing all service accounts in our project. Let’s click the three dots near to the recently created Pub/Sub service account and click Create key:
As the key type, let’s select the JSON:
As the last step, please download the file and put it inside the resources directory of the Spring Boot project.
3. Imports
If all the Pub/Sub steps above have been completed, we can switch to the Spring Boot project. Firstly, let’s import two dependencies we will need for our project to work:
implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.cloud:spring-cloud-gcp-starter-pubsub'
4. Configure Application Properties
As the next step, let’s define the application.yaml file as follows:
pubsub-example: subscription: name: ${EXAMPLE_PUBSUB_SUBSCRIPTION} topic: name: ${EXAMPLE_PUBSUB_TOPIC} spring: cloud: gcp: credentials: location: classpath:credentials.json
Please notice, the name of the JSON should match the name of the file that we’ve put inside the resources folder. Also, the environment variables describing subscription and topic should be set to the correct values, for example:
- EXAMPLE_PUBSUB_SUBSCRIPTION -> projects/[PROJECT-ID]/subscriptions/example-subscription
- EXAMPLE_PUBSUB_TOPIC -> projects/[PROJECT-ID]/topics/example
6. Create a POJO Class
Our next step is to create a simple POJO class, which will be used for the serialization and deserialization of the messages sent through the Pub/Sub:
public class MessageEntity { private final LocalDateTime timestamp; private final String message; //constructor, getters, setters }
As you can see, it is a simple class containing the timestamp and the value of the message.
7. Set Up The Consumer
Let’s start the work on the consumer part by creating the PubSubConsumer interface:
public interface PubSubConsumer { String subscription(); Class payloadType(); Consumer<ConvertedBasicAcknowledgeablePubsubMessage> messageConsumer(); }
As you can see, this interface consists of three methods definitions:
- subscription -> returns the name of the subscription
- payloadTyme -> returns the Class object representing the target type of the payload
- messageConsumer -> returns the callback method triggered when new messages arrive
Technically, we don’t need this interface, but with this approach, we will be able to add easily more consumers in the future.
Secondly, let’s create the ExampleCosumer class implementing the interface:
@Component public class ExampleConsumer implements PubSubConsumer { private static final Logger log = LoggerFactory.getLogger(ExampleConsumer.class); private final String subscriptionName; public ExampleConsumer( @Value("${pubsub-example.subscription.name}") String subscriptionName ) { this.subscriptionName = subscriptionName; } }
As you might have noticed, our class contains two fields: a logger and a subscriptionName which will be bound with the value from our properties.yaml file.
Please remember, that our class implements PubSubConsumer, so as the next step we need to provide the definition for each method:
@Override public String subscription() { return subscriptionName; } @Override public Class payloadType() { return MessageEntity.class; } @Override public Consumer<ConvertedBasicAcknowledgeablePubsubMessage> messageConsumer() { return this::consume; }
To keep our code more readable, let’s implement our consumer method as a separate function:
private void consume(ConvertedBasicAcknowledgeablePubsubMessage message) { MessageEntity received = message.getPayload(); log.info( "Received entity: [timestamp= {}, message= {}]", received.getTimestamp(), received.getMessage() ); message.ack(); }
To put it simply, this method extracts the payload from the ConvertedBasicAcknowledgeablePubsubMessage object and logs the timestamp and the message at the INFO level. In the end, it asynchronously acknowledges the message.
As the next step, let’s create ExampleSubscriberConfig class and put the configuration for our subscriber there:
@Configuration public class ExampleSubscriberConfig { private static final Logger log = LoggerFactory.getLogger(ExampleSubscriberConfig.class); private final PubSubTemplate pubSubTemplate; private final PubSubConsumer exampleConsumer; //constructor }
As you might have noticed, besides the logger and the exampleConsumer, we are injecting pubsubTemplate, which will be used for publishing to topics and consuming messages.
Let’s implement the method responsible for subscription:
@EventListener(ApplicationReadyEvent.class) public void subscribe() { log.info("Subscribing to {}", exampleConsumer.subscription()); pubSubTemplate.subscribeAndConvert( exampleConsumer.subscription(), exampleConsumer.messageConsumer(), exampleConsumer.payloadType() ); }
The annotation EventListener used with ApplicationReadyEvent indicates, that the method will be called when the application is ready to receive requests.
As the last step, we need to provide the bean definition for a converter using Jackson JSON:
@Configuration public class PubSubConfig { @Bean public JacksonPubSubMessageConverter jacksonPubSubMessageConverter(ObjectMapper objectMapper) { return new JacksonPubSubMessageConverter(objectMapper); } }
With that being done, we are ready to receive the messages.
8. Set Up The Publisher
Besides receiving the messages, it would be good to have the possibility to publish some. Let’s start the work on the publisher part by creating the ExamplePublisher class:
@Component public class ExamplePublisher { private final String topic; private final PubSubTemplate pubSubTemplate; public ExamplePublisher( @Value("${pubsub-example.topic.name}") String topic, PubSubTemplate pubSubTemplate) { this.topic = topic; this.pubSubTemplate = pubSubTemplate; } }
As we can see, we need to inject the PubSubTemplate bean and the topic, which will be set to the value from the application properties.
Let’s add a publish method then. It will take the MessageEntity as the argument:
public void publish(MessageEntity payload) { pubSubTemplate.publish(topic, payload); }
As you might have noticed, this method uses the publish method to push the object to the specified Pub/Sub topic.
9. Create Example Controller
And finally, we can implement the TestController class:
@RestController @RequestMapping("/api") public class TestController { private final ExamplePublisher publisher; public TestController(ExamplePublisher publisher) { this.publisher = publisher; } @PostMapping("/topic") public void publish(@RequestBody String message) { MessageEntity entity = new MessageEntity(LocalDateTime.now(), message); publisher.publish(entity); } }
It’s just a simple REST controller, which will be responding to the POST /api/topic calls and forwarding the message to the publisher.
10. Testing Using Curl
Lastly, there is nothing else to do than just test our application with the following command:
curl -d 'Some message' localhost:8080/api/topic
After it executes, we should see a new entry in our application logs (and each time we call the endpoint as well).
9. GCP Pub/Sub With Spring Boot Conclusion
And I believe, that’s all for today. In this step-by-step tutorial, we’ve learned how to publish and receive messages through the Google Cloud Pub/Sub using the Spring Boot Java application.
I hope that you’ve really enjoyed this guide. I would be forever grateful if you would like to share some feedback with me. Would you like to see more Java tutorials as well? Please, let me know about it using our page, group, or contact form.
For the source code of the project, please visit our project on GitHub.
2 Responses
Nice helpful description. It helped me.
Happy to hear that brother!