Configure Spring Boot #KafkaListener for listening to the latest messages - spring-boot

I am using Spring Boot's #KafkaListener to monitor some server's heartbeat messages as:
#KafkaListener(topics = "heartbeat-topic", groupId = "monitor")
public void listenToHeartbeatMsg(String message) {}
Issue is when the listener subscriber application started, even though the server is down, subscriber application will be receiving those previous heartbeat messages.
How can I fix this issue and listen only for real-time heartbeat messages?

Implement ConsumerSeekAware and, in onPartitionsAssigned call seekToBeginning on the callback.
See https://docs.spring.io/spring-kafka/docs/current/reference/html/#seek
public class MyListener implements ConsumerSeekAware {
...
#Override
public void onPartitionsAssigned(Map<TopicPartition, Long> assignments, ConsumerSeekCallback callback) {
callback.seekToEnd(assignments.keySet());
}
}

Related

JMS with spring boot, sender and receiver on same package: what is its use?

I am learning JMS with spring boot and nice to know that spring boot comes with embed Active MQ JMS broker.
I started from spring page on how to achieve this and it works like charm. Now i went little further and create two separate spring boot application one containing jms sender code and another containing receiver code.
I tried starting and application failed as both application are using same port for JMS. I fixed this by including this on one application
#Bean
public BrokerService broker() throws Exception {
final BrokerService broker = new BrokerService();
broker.addConnector("tcp://localhost:61616");
broker.addConnector("vm://localhost");
broker.setPersistent(false);
return broker;
}
But now sender is sending message successfully but receiver is doing nothing. I search on stackoverflow and look at this and this. And they are saying:
If you want to use JMS in production, it would be much wiser to avoid using Spring Boot embedded JMS brokers and host it separately. So 3 node setup would be preferred for PROD.
So my questions are:
1. What is the purpose of putting both jms sender and receiver on same application? Is there any practical example
2. Is it really not possible to use spring boot embedded JMS to communicate two separate application.
You might have sender and receiver in the same application if requests arrive in bursts and you want to save them somewhere before they are processed, in case of a server crash. You typically still wouldn't use an embedded broker for that.
Embedded brokers are usually used for testing only.
You can, however, run an embedded broker that is accessible externally; simply fire up a BrokerService as you have, but the other app needs to connect with the tcp://... address, not the vm://....
EDIT
App1:
#SpringBootApplication
#RestController
public class So52654109Application {
public static void main(String[] args) {
SpringApplication.run(So52654109Application.class, args);
}
#Bean
public BrokerService broker() throws Exception {
final BrokerService broker = new BrokerService();
broker.addConnector("tcp://localhost:61616");
broker.setPersistent(false);
broker.start();
return broker;
}
#Autowired
private JmsTemplate template;
#RequestMapping(path = "/foo/{id}")
public String foo(#PathVariable String id) {
template.convertAndSend("someQueue", id);
return id + ": thank you for your request, we'll send an email to the address on file when complete";
}
}
App2:
application.properties
spring.activemq.broker-url=tcp://localhost:61616
and
#SpringBootApplication
public class So526541091Application {
public static void main(String[] args) {
SpringApplication.run(So526541091Application.class, args);
}
#JmsListener(destination = "someQueue")
public void process(String id) {
System.out.println("Processing request for id");
}
}
Clearly, for a simple app like this you might just run the listener in the first app.
However, since there is no persistence of messages with this configuration, you would likely use an external broker for a production app (or enable persistence).

Spring Cloud Stream topic per message for different consumers

The topology I am looking for is
So far I have not seen a way to define the topic per message in Cloud Stream. I understand that the consumers will be bound to specific topic but how does the producer sets the topic per message before sending the message to the exchange?
source.output().send(MessageBuilder.withPayload(myMessage).build());
Does not provide any way to set the topic for the exchange to route to the proper consumer.
Or maybe I don't understand something correctly?
UPDATE
I would expect not to receive the message in the consumer due to the bindingRoutingKey being 2222 and I am sending with routeTo 1111. But I still receive it on the consumer.
Producer Properties:
spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
spring.cloud.stream.bindings.output.content-type=application/json
spring.cloud.stream.bindings.output.destination=messageExchange
spring.cloud.stream.rabbit.bindings.output.producer.routing-key-expression=headers['routeTo']
#EnableBinding(Source.class)
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Sender:
source.output().send(MessageBuilder.withPayload(mo).setHeader("routeTo", "1111").build());
And the Consumer:
spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
spring.cloud.stream.bindings.input.destination=messageExchange
spring.cloud.stream.rabbit.bindings.input.consumer.bindingRoutingKey=2222
Application:
#SpringBootApplication
#EnableBinding(Sink.class)
public class Application {
private static final Logger log = LoggerFactory.getLogger(Application.class);
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#StreamListener(Sink.INPUT)
public void ReceiveMo(String moDTO) {
log.info("Message received moDTO: {}", moDTO);
}
}
SECOND UPDATE
With the suggestions in the accepted answer below. I was able to make it work. Needed to remove the exchanges and queues from RabbitMQ using its UI and restart the RabbitMQ docker image.
The the routingKeyExpression rabbitmq producer property.
e.g. ...producer.routing-key-expression=headers['routeTo']
then
source.output().send(MessageBuilder.withPayload(myMessage)
.setHeader("routeTo", "Booking.new")
.build());
Note that the destination is the exchange name. By default, the binder expects a Topic exchange. If you wish to use a Direct exchange instead, you must set the exchangeType property.

Spring Boot WebSockets notifications

In my Spring Boot application I'm trying to implement a notifications functionality based on WebSockets.
I have provided a following configuration:
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/notifications").withSockJS();
}
#Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.enableSimpleBroker("/topic", "/queue");
}
}
and trying to use SimpMessagingTemplate in order to send a message from server side to a specific client(user).
#Autowired
private SimpMessagingTemplate simpMessagingTemplate;
public void sendMessages() {
simpMessagingTemplate.convertAndSendToUser(%user%, "/horray", "Hello, World!");
}
Right now I don't understand a few things:
What value should be used for %user% parameter of
simpMessagingTemplate.convertAndSendToUser method ?
What is the correlation between my /notifications endpoint
registered in WebSocketConfig.registerStompEndpoints method and
destination parameter of
simpMessagingTemplate.convertAndSendToUser method and how to properly use it?
How to protect the users from reading other people's messages on the
client ?
The user parameter is the name that the client use when he subscribes the destination, see Spring Reference Chapter 26.4.11 User Destinations
Destination vs Endpoint:
Endpoint is the url where the websocket/message brocker is listening
Destination is the topic or subject within the message brocker

Spring websocket send message from async method

I am using spring websocket 4.2.4 with sockjs and stomp and i am trying to send a message from the server to all the subscribers in an async task with no luck
my class is:
public class MyClass{
private timer;
public MyClass(){
this.timer = new Timer();
}
#Async
public void myMethod(){
timer.schedule(new MyReminder(), 1000);
}
#Async
private class MyReminder extends TimerTask{
#Autowired
SimpMessagingTemplate messageingTemplate;
#Override
public void run(){
messageingTemplate.convertAndSend("/app/subscribers","message");
}
}
}
but the subscribers dont get the message
any help? what did i do wrong :(
* EDIT *
my message broker:
public void configureMessageBroker(MessageBrokerRegistry config){
config.enableSimpleBroker("/topic","/myApp");
config.setApplicationDestinationPrefixes("/myApp");
}
and when i subscribe:
subscribe("myApp/someRoute")
thanks!!
** Edit 2: **
thanks for the help i fix the problem :)
You can do that but over Broker destination.
Share, please, your AbstractWebSocketMessageBrokerConfigurer.
Typically we do this:
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.setApplicationDestinationPrefixes("/app/")
.enableSimpleBroker("/queue/", "/topic/");
}
Where all those /app/ destinations won't be treated by Broker. You can subscribe to them only in case of #SubscribeMapping - the request-reply scenario from the client initiative.
Your task is fully fits to the publish-subscribe story - topic in terms of STOMP.
There fore your subscribers (clients) should be subscribed to some topic on the broker and after that you can simply send message to it.

Declaration of exchanges and queues in Spring AMQP

I'm using RabbitMQ and trying to refactor my current native java implementation to using the Spring AMQP abstraction.
Declaration of exchanges, queues and their binding using the Spring library is via the AMQPAdmin interface, but I'm not sure when this sort of configuration should happen.
I have a web application that uses Rabbit to produce messages. And another app that consumes these messages. Shocker :)
But when show the declaration of the exchanges/queues take place?
Do I deploy the AMQPAdmin with the web applications and do exchange/queue administration within constructors of producers and consumers?
Declaration of these things are a one off, the broke doesn't need to know about them again, so any code would be a NOOP on subsequent executions.
Do I create a separate application for administration of the broker?
What is the current thinking or best practices here?
It would appear that very few people are using Spring's AMQP M1 release, so I will answer my own question with what I've done.
In the producer's constructor I declare the exchange. Then set the exchange on the RabbitTemplate. I also set the routing key on the RabbitTemplate as the queue name, but that isn't required, but it was the route I would be using.
#Service("userService")
public class UserService {
private final RabbitTemplate rabbitTemplate;
#Autowired
public UserService(final RabbitAdmin rabbitAdmin,
final Exchange exchange,
final Queue queue,
#Qualifier("appRabbitTemplate") final RabbitTemplate rabbitTemplate) {
this.rabbitTemplate = rabbitTemplate;
rabbitAdmin.declareExchange(exchange);
rabbitTemplate.setExchange(exchange.getName());
rabbitTemplate.setRoutingKey(queue.getName());
}
public void createAccount(final UserAccount userAccount) {
rabbitTemplate.convertAndSend("Hello message sent at " + new DateTime());
}
}
In the consumer's constructor I declare the queue and create the binding.
public class Consumer implements ChannelAwareMessageListener<Message> {
public Consumer(final RabbitAdmin rabbitAdmin, final Exchange exchange, final Queue queue) {
rabbitAdmin.declareQueue(queue);
rabbitAdmin.declareBinding(BindingBuilder.from(queue).to((DirectExchange) exchange).withQueueName());
}
#Override
public void onMessage(Message message, Channel channel) throws Exception {
System.out.println(new String(message.getBody()));
channel.basicAck(message.getMessageProperties().getDeliveryTag(), true);
}
}
Although the constructors may be run many times, RabbitMQ only declares the exchange, queue and bindings once.
If you need the whole source for this little example project, ask, and I'll put it up somewhere for you.

Resources