RabbitMQ slow receiving speed - java

I need to create application which should receive data from a data producer and process it. I chose RabbitMQ as a message-broker. My tests show me not the best results:
Sent - 100 msg;
Produce - 100 msg/s;
Consume - 6 msg/s;
To solve it I set listenerContainer.setAcknowledgeMode(AcknowledgeMode.NONE);
But I need acknowledge for some queues. And I can't do it in parallel using workers, because the order of messages is important for the data processing.
Is it possible to increase the speed of receiving, having acknowledges?
Producer:
#Bean
Queue queue() {
return new Queue(queueName, false);
}
#Bean
public ConnectionFactory connectionFactory() {
CachingConnectionFactory connectionFactory = new CachingConnectionFactory("ip");
connectionFactory.setUsername("name");
connectionFactory.setPassword("pswd");
return connectionFactory;
}
#Bean
public AmqpAdmin amqpAdmin() {
return new RabbitAdmin(connectionFactory());
}
#Bean
public RabbitTemplate rabbitTemplate() {
return new RabbitTemplate(connectionFactory());
}
#Bean
public FanoutExchange exchange() {
return new FanoutExchange("exchange-1");
}
#Bean
public Binding binding(){
return BindingBuilder.bind(queue()).to(exchange());
}
...
rabbitTemplate.setExchange("exchange-1");
rabbitTemplate.convertAndSend(data);
Consumer:
#Bean
public ConnectionFactory connectionFactory() {
CachingConnectionFactory connectionFactory = new CachingConnectionFactory("ip");
connectionFactory.setUsername("name");
connectionFactory.setPassword("pswd");
return connectionFactory;
}
#Bean
public RabbitTemplate rabbitTemplate() {
return new RabbitTemplate(connectionFactory());
}
#Bean
Queue queue() {
return new Queue("queue-1", false);
}
#Bean
public SimpleMessageListenerContainer listenerContainer() {
SimpleMessageListenerContainer listenerContainer = new SimpleMessageListenerContainer();
listenerContainer.setConnectionFactory(connectionFactory());
listenerContainer.setQueues(queue());
listenerContainer.setMessageListener(new Receiver());
listenerContainer.setAcknowledgeMode(AcknowledgeMode.AUTO);
return listenerContainer;
}
...
#Override
public void onMessage(Message message) {
System.out.println("Received message: " + fromBytes(message.getBody()) + " \n Time = " + System.currentTimeMillis());
}
Tested on instance with 2 vCPU and 4 Gb memory.

You can increase the container's prefetchCount, which will greatly improve performance. However, if you reject and requeue a message, ordering will be lost (the requeued message will be behind the prefetched messages).

Related

DefaultJmsListnerContainerFactory concurrency not working

This is my JMS configuration:
#EnableJms
#Configuration
public class VmsJmsConfig implements JmsListenerConfigurer {
#Value("${spring.activemq.broker-url}")
String brokerUrl;
#Value("${spring.activemq.ssl.trustStorePath}")
String trustStorePath;
#Value("${spring.activemq.ssl.trustStorePass}")
String trustStorePass;
#Bean
public DefaultJmsListenerContainerFactory defaultJmsListenerContainerFactory(ConnectionFactory conFactory) {
DefaultJmsListenerContainerFactory defaultJmsListenerContainerFactory = new DefaultJmsListenerContainerFactory();
defaultJmsListenerContainerFactory.setConnectionFactory(conFactory);
defaultJmsListenerContainerFactory.setConcurrency("10-20");
return defaultJmsListenerContainerFactory;
}
#Bean("conFactory")
public ConnectionFactory activeMQSslConnectionFactory() throws Exception {
ActiveMQSslConnectionFactory activeMQSslConnectionFactory = new ActiveMQSslConnectionFactory(brokerUrl);
activeMQSslConnectionFactory.setTrustStore(trustStorePath);
activeMQSslConnectionFactory.setTrustStorePassword(trustStorePass);
return activeMQSslConnectionFactory;
}
#Bean
public DefaultMessageHandlerMethodFactory handlerMethodFactory() {
DefaultMessageHandlerMethodFactory factory = new DefaultMessageHandlerMethodFactory();
factory.setMessageConverter(messageConverter());
return factory;
}
#Bean
public MessageConverter messageConverter() {
return new MappingJackson2MessageConverter();
}
#Override
public void configureJmsListeners(JmsListenerEndpointRegistrar registrar) {
registrar.setMessageHandlerMethodFactory(handlerMethodFactory());
}
}
My app is listening to an ActiveMQ queue. It consumes a message, transforms it, sends a downstream request, waits for the response, and sends that response to another queue.
I want it to consume multiple messages at the same time and process them in parallel, but no matter how many consumer I set in setConcurrency() it only consumes 1 message at a time, and there are more than 1000 messages pending in queue.
I tried changing the concurency, but no luck. But when I comment downstream call, it consumes 10-20 messages at a time, I couldn't find reason for that.

Cause: Cannot determine reply destination: Request message does not contain reply-to destination, and no default reply destination set

Using JMSConfig i am creating MQ Connection factory and i have InboundGatewayConfig and OutboundGatewayConfig , in Inbound Config i am reading the Message payload from one queue and in Outbound config sending the message to another queue and my goad is toset reply channel to acknowledge the sender queue once queue receive the message.
InboundGatewayConfig:
#Configuration
#EnableIntegration
public class InboundGatewayConfig {
#Autowired
JmsConfig jmsConfig;
#Value("${fcb.inbound.receiver.queue.name}")
private String orderRequestDestination;
#Bean
public MessageChannel inboundOrderRequestChannel() {
return new DirectChannel();
}
#Bean
public MessageChannel inboundOrderResponseChannel() {
return new DirectChannel();
}
#Bean
#ServiceActivator(inputChannel = "inboundOrderRequestChannel")
public OrderService orderService() {
return new OrderService();
}
#Bean
public JmsInboundGateway jmsInboundGateway() {
JmsInboundGateway gateway = new JmsInboundGateway(
defaultMessageListenerContainer(),
channelPublishingJmsMessageListener());
gateway.setRequestChannel(inboundOrderRequestChannel());
return gateway;
}
#Bean
public DefaultMessageListenerContainer defaultMessageListenerContainer() {
DefaultMessageListenerContainer container = new DefaultMessageListenerContainer();
container.setConnectionFactory(jmsConfig.fcbCachingConnectionFactory());
container.setDestinationName(orderRequestDestination);
container.setRecoveryInterval(5000);
container.setErrorHandler(t -> System.out.println("Error in JMS Configurations \t" + t.getCause()));
return container;
}
#Bean
public ChannelPublishingJmsMessageListener channelPublishingJmsMessageListener() {
ChannelPublishingJmsMessageListener channelPublishingJmsMessageListener = new ChannelPublishingJmsMessageListener();
channelPublishingJmsMessageListener.setExpectReply(true);
return channelPublishingJmsMessageListener;
}
}
OutboundGatewayConfig :
#Configuration
public class OutboundGatewayConfig {
#Value("${fcb.inbound.receiver.queue.name}")
private String orderRequestDestination;
#Value("${fcb.inbound.response.queue.name}")
private String orderResponseDestination;
#Autowired
JmsConfig jmsConfig;
#Bean
public MessageChannel outboundOrderRequestChannel() {
return new DirectChannel();
}
#Bean
public MessageChannel outboundOrderResponseChannel() {
return new QueueChannel();
}
#Bean
#ServiceActivator(inputChannel = "outboundOrderRequestChannel")
public JmsOutboundGateway jmsOutboundGateway() {
JmsOutboundGateway gateway = new JmsOutboundGateway();
gateway.setConnectionFactory(jmsConfig.fcbCachingConnectionFactory());
gateway.setRequestDestinationName(orderRequestDestination);
gateway.setReplyDestinationName(orderResponseDestination);
gateway.setReplyChannel(outboundOrderResponseChannel());
return gateway;
}
}
and this my service class
public class OrderService {
private static final Logger LOGGER =
LoggerFactory.getLogger(OrderService.class);
public Message<?> order(Message<?> order) {
LOGGER.info("received order='{}'", order);
Message<?> status = MessageBuilder.withPayload("Accepted")
.setHeader("jms_correlationId",
order.getHeaders().get("jms_messageId"))
.setReplyChannelName("inboundOrderResponseChannel").build();
LOGGER.info("sending status='{}'", status);
return status;
}
}
While running the application InboundGateway able to receive the message payload from request queue but while reply , giving bellow warning message.
2022-04-07 16:06:24.303 WARN 19408 --- [enerContainer-1] o.s.j.l.DefaultMessageListenerContainer : Setup of JMS message listener invoker failed for destination 'xxx.yyy.queue' - trying to recover. Cause: Cannot determine reply destination: Request message does not contain reply-to destination, and no default reply destination set.

Spring - plain RabbitMQ a lot faster than plain RabbitMQ + JMS?

I have 2 Spring RabbitMq configurations, one using the RabbitTemplate, one using the JmsTemplate.
The configuration with the RabbitTemplate:
Class AmqpMailIntegrationPerfTestConfig:
#Configuration
#ComponentScan(basePackages = {
"com.test.perf.amqp.receiver",
"com.test.perf.amqp.sender"
})
#EnableRabbit
public class AmqpMailIntegrationPerfTestConfig {
#Bean
public DefaultClassMapper classMapper() {
DefaultClassMapper classMapper = new DefaultClassMapper();
Map<String, Class<?>> idClassMapping = new HashMap<>();
idClassMapping.put("mail", MailMessage.class);
classMapper.setIdClassMapping(idClassMapping);
return classMapper;
}
#Bean
public Jackson2JsonMessageConverter jsonMessageConverter() {
Jackson2JsonMessageConverter jsonConverter = new Jackson2JsonMessageConverter();
jsonConverter.setClassMapper(classMapper());
return jsonConverter;
}
#Bean
public RabbitTemplate myRabbitTemplate(ConnectionFactory connectionFactory) {
final RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
rabbitTemplate.setMessageConverter(jsonMessageConverter());
return rabbitTemplate;
}
#Bean
public ConnectionFactory createConnectionFactory(){
CachingConnectionFactory connectionFactory = new CachingConnectionFactory("localhost");
return connectionFactory;
}
#Bean
Queue queue() {
return new Queue(AmqpMailSenderImpl.QUEUE_NAME, false);
}
#Bean
TopicExchange exchange() {
return new TopicExchange(AmqpMailSenderImpl.TOPIC_EXCHANGE_NAME);
}
#Bean
Binding binding(Queue queue, TopicExchange exchange) {
return BindingBuilder.bind(queue).to(exchange).with(AmqpMailSenderImpl.ROUTING_KEY);
}
#Bean
public AmqpAdmin amqpAdmin() {
return new RabbitAdmin(createConnectionFactory());
}
#Bean
public SimpleRabbitListenerContainerFactory myRabbitListenerContainerFactory() {
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setConnectionFactory(createConnectionFactory());
factory.setMaxConcurrentConsumers(5);
factory.setMessageConverter(jsonMessageConverter());
return factory;
}
}
The AmqpMailSenderPerfImpl class in com.test.perf.amqp.sender package:
#Component
public class AmqpMailSenderPerfImpl implements MailSender {
public static final String TOPIC_EXCHANGE_NAME = "mails-exchange";
public static final String ROUTING_KEY = "mails";
#Autowired
private RabbitTemplate rabbitTemplate;
#Override
public boolean sendMail(MailMessage message) {
rabbitTemplate.convertAndSend(TOPIC_EXCHANGE_NAME, ROUTING_KEY, message);
return true;
}
}
The AmqpMailReceiverPerfImpl class in com.test.perf.amqp.receiver package:
#Component
public class AmqpMailReceiverPerfImpl implements ReceivedDatesKeeper {
private Logger logger = LoggerFactory.getLogger(getClass());
private Map<String,Date> datesReceived = new HashMap<String, Date>();
#RabbitListener(containerFactory = "myRabbitListenerContainerFactory", queues = AmqpMailSenderImpl.QUEUE_NAME)
public void receiveMessage(MailMessage message) {
logger.info("------ Received mail! ------\nmessage:" + message.getSubject());
datesReceived.put(message.getSubject(), new Date());
}
public Map<String, Date> getDatesReceived() {
return datesReceived;
}
}
The configuration with the JmsTemplate:
Class JmsMailIntegrationPerfTestConfig:
#Configuration
#EnableJms
#ComponentScan(basePackages = {
"com.test.perf.jms.receiver",
"com.test.jms.sender"
})
public class JmsMailIntegrationPerfTestConfig {
#Bean
public MessageConverter jacksonJmsMessageConverter() {
MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
Map<String,Class<?>> typeIdMappings = new HashMap<String,Class<?>>();
typeIdMappings.put("mail", MailMessage.class);
converter.setTypeIdMappings(typeIdMappings);
converter.setTargetType(MessageType.TEXT);
converter.setTypeIdPropertyName("_type");
return converter;
}
#Bean
public ConnectionFactory createConnectionFactory(){
RMQConnectionFactory connectionFactory = new RMQConnectionFactory();
connectionFactory.setUsername("guest");
connectionFactory.setPassword("guest");
connectionFactory.setVirtualHost("/");
connectionFactory.setHost("localhost");
connectionFactory.setPort(5672);
return connectionFactory;
}
#Bean(name = "myJmsFactory")
public JmsListenerContainerFactory<?> myFactory(ConnectionFactory connectionFactory) {
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
factory.setConnectionFactory(connectionFactory);
factory.setConcurrency("10-50");
factory.setMessageConverter(jacksonJmsMessageConverter());
return factory;
}
#Bean
public Destination jmsDestination() {
RMQDestination jmsDestination = new RMQDestination();
jmsDestination.setDestinationName("myQueue");
jmsDestination.setAmqp(false);
jmsDestination.setAmqpQueueName("mails");
return jmsDestination;
}
#Bean
public JmsTemplate myJmsTemplate(ConnectionFactory connectionFactory) {
final JmsTemplate jmsTemplate = new JmsTemplate(connectionFactory);
jmsTemplate.setMessageConverter(jacksonJmsMessageConverter());
return jmsTemplate;
}
}
The JmsMailSenderImpl class in package com.test.jms.sender:
#Component
public class JmsMailSenderImpl implements MailSender {
private Logger logger = LoggerFactory.getLogger(getClass());
#Autowired
private JmsTemplate jmsTemplate;
#Override
public boolean sendMail(MailMessage message) {
logger.info("Sending message!");
jmsTemplate.convertAndSend("mailbox", message);
return false;
}
}
The JmsMailReceiverPerfImpl class in package com.test.perf.jms.receiver:
#Component
public class JmsMailReceiverPerfImpl implements ReceivedDatesKeeper {
private Logger logger = LoggerFactory.getLogger(getClass());
private Map<String,Date> datesReceived = new HashMap<String, Date>();
#JmsListener(destination = "mailbox", containerFactory = "myJmsFactory", concurrency = "10")
public void receiveMail(MailMessage message) {
datesReceived.put(message.getSubject(), new Date());
logger.info("Received <" + message.getSubject() + ">");
}
public Map<String, Date> getDatesReceived() {
return datesReceived;
}
}
I test the above configurations by starting 10 threads and making the respective MailSenders send 1000 mails each.
For the config with the RabbitTemplate I get:
* Total throughput time of all messages: 3687ms
* Time to process one message: 817ms
For the config with the JmsTemplate I get:
* Total throughput time of all messages: 41653ms
* Time to process one message: 67ms
This seems to indicate that the version with the JmsTemplate is not working in parallel, or at least, does not use resources optimally.
Does anybody know what could be causing this? I played around with different transaction and concurrency parameters but to no avail.
What we want is to get the same throughput time with the JmsTemplate as with the RabbitTemplate, so we can use JMS as an abstraction layer.
I can see why the consumer side is slower - the Consumer.receive() uses a synchronous basicGet() for each message whereas the #RabbitListener container uses basicConsume with a prefetch count of 250.
On the JMS sending side, you need to use a CachingConnectionFactory there are well, otherwise a new session/producer/channel is created for each send.
It's still quite a bit slower, though, even with that; I suggest you ask on the rabbitmq-users Google group where the RabbitMQ engineers hang out. They maintain the JMS client.

Giving incorrect(non-existing) queue name while publishing in RabbitMQ

I'm using AyncRabbitTemplate for publishing messages. Giving an incorrect(non-existing) queue name while publishing - it drops the message silently.
I have tried enabling "confirm" and "mandate" on the AyncRabbitTemplate and added the required callback methods as below:
#Bean
AsyncRabbitTemplate template() {
RabbitTemplate rabbit = rabbitTemplate();
rabbit.setChannelTransacted(true); //to throw error when channel shuts down in case of incorrect exchange names
AsyncRabbitTemplate asyncRabbitTemplate = new AsyncRabbitTemplate(rabbit, rpcReplyMessageListenerContainer(connectionFactory()));
asyncRabbitTemplate.setEnableConfirms(true);
asyncRabbitTemplate.setMandatory(true); //if the message cannot be delivered to a queue an AmqpMessageReturnedException will be thrown
return asyncRabbitTemplate;
}
#Bean
public SimpleMessageListenerContainer rpcReplyMessageListenerContainer(ConnectionFactory connectionFactory) {
SimpleMessageListenerContainer simpleMessageListenerContainer = new SimpleMessageListenerContainer(connectionFactory);
simpleMessageListenerContainer.setQueueNames(Constants.REPLY_QUEUE);
simpleMessageListenerContainer.setTaskExecutor(Executors.newCachedThreadPool());
simpleMessageListenerContainer.setAcknowledgeMode(AcknowledgeMode.AUTO);
return simpleMessageListenerContainer;
}
#Bean
public RabbitTemplate rabbitTemplate() {
return new RabbitTemplate(connectionFactory());
}
#Bean
public ConnectionFactory connectionFactory() {
CachingConnectionFactory connectionFactory = new CachingConnectionFactory("localhost");
return connectionFactory;
}
And the callback methods as:
RabbitConverterFuture<String> future = this.asyncRabbitTemplate.convertSendAndReceive("",Constants.SNS_QUEUE, "This is the request message ",new MessagePostProcessor() {
#Override
public Message postProcessMessage(Message message) {
message.getMessageProperties().setTimestamp(new Date());
message.getMessageProperties().setMessageId(UUID.randomUUID().toString());
return message;
}
});
ListenableFuture<Boolean> future2 = future.getConfirm();
future2.addCallback(new ListenableFutureCallback<Boolean>() {
#Override
public void onSuccess(Boolean result) {
System.out.println("Publish Result " + result);
}
#Override
public void onFailure(Throwable ex) {
System.out.println("Publish Failed: " + ex);
}
});
As discussed in the documentation you have to enable returned messages on the connection factory.
The send will not throw an exception but undeliverable messages will be returned to the ReturnCallback (if mandatory is true).
Confirms are not sent for undeliverable messages. A negative confirmation is only received if there is some kind of problem in the broker; they are rare.

How to create a Spring Reactor Flux from a ActiveMQ queue?

I am experimenting with the Spring Reactor 3 components and Spring Integration to create a reactive stream (Flux) from a JMS queue.
I am attempting to create a reactive stream (Spring Reactor 3 Flux) from a JMS queue (ActiveMQ using Spring Integration) for clients to get the JMS messages asynchronously. I believe that I have everything hooked up correctly but the client does not receive any of the JMS messages until the server is stopped. Then all of the messages get "pushed" to the client a once.
Any help would be appreciated.
Here is the configuration file that I am using to configure the JMS, Integration components and the reactive publisher:
#Configuration
#EnableJms
#EnableIntegration
public class JmsConfiguration {
#Value("${spring.activemq.broker-url:tcp://localhost:61616}")
private String defaultBrokerUrl;
#Value("${queues.patient:patient}")
private String patientQueue;
#Autowired
MessageListenerAdapter messageListenerAdapter;
#Bean
public DefaultJmsListenerContainerFactory myFactory(
DefaultJmsListenerContainerFactoryConfigurer configurer) {
DefaultJmsListenerContainerFactory factory =
new DefaultJmsListenerContainerFactory();
configurer.configure(factory, jmsConnectionFactory());
return factory;
}
#Bean
public Queue patientQueue() {
return new ActiveMQQueue(patientQueue);
}
#Bean
public ActiveMQConnectionFactory jmsConnectionFactory() {
ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory();
connectionFactory.setBrokerURL(defaultBrokerUrl);
connectionFactory.setTrustedPackages(Arrays.asList("com.sapinero"));
return connectionFactory;
}
// Set the jackson message converter
#Bean
public JmsTemplate jmsTemplate() {
JmsTemplate template = new JmsTemplate();
template.setConnectionFactory(jmsConnectionFactory());
template.setDefaultDestinationName(patientQueue);
template.setMessageConverter(jacksonJmsMessageConverter());
return template;
}
#Bean
public MessageListenerAdapter messageListenerAdapter() {
MessageListenerAdapter messageListenerAdapter = new MessageListenerAdapter();
messageListenerAdapter.setMessageConverter(jacksonJmsMessageConverter());
return messageListenerAdapter;
}
#Bean
public AbstractMessageListenerContainer messageListenerContainer() {
DefaultMessageListenerContainer defaultMessageListenerContainer = new DefaultMessageListenerContainer();
defaultMessageListenerContainer.setMessageConverter(jacksonJmsMessageConverter());
defaultMessageListenerContainer.setConnectionFactory(jmsConnectionFactory());
defaultMessageListenerContainer.setDestinationName(patientQueue);
defaultMessageListenerContainer.setMessageListener(messageListenerAdapter());
defaultMessageListenerContainer.setCacheLevel(100);
defaultMessageListenerContainer.setErrorHandler(new ErrorHandler() {
#Override
public void handleError(Throwable t) {
t.printStackTrace();
}
});
return defaultMessageListenerContainer;
}
#Bean // Serialize message content to json using TextMessage
public MessageConverter jacksonJmsMessageConverter() {
MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
converter.setTargetType(MessageType.TEXT);
converter.setTypeIdPropertyName("_type");
return converter;
}
#Bean
public MessageChannel jmsOutboundInboundReplyChannel() {
return MessageChannels.queue().get();
}
#Bean
public Publisher<Message<String>> pollableReactiveFlow() {
return IntegrationFlows
.from(Jms.messageDrivenChannelAdapter(messageListenerContainer()).get())
.channel(MessageChannels.queue())
.log(LoggingHandler.Level.DEBUG)
.log()
.toReactivePublisher();
}
#Bean
public MessageChannel jmsChannel() {
return new DirectChannel();
}
The controller that creates the Flux is:
#RestController
#RequestMapping("patients")
public class PatientChangePushController {
private LocalDateTime lastTimePatientDataRetrieved = LocalDateTime.now();
private int durationInSeconds = 30;
private Patient patient;
AtomicReference<SignalType> checkFinally = new AtomicReference<>();
#Autowired
PatientService patientService;
#Autowired
#Qualifier("pollableReactiveFlow")
private
Publisher<Message<String>> pollableReactiveFlow;
#Autowired
private JmsTemplate jmsTemplate;
#Autowired
private Queue patientQueue;
/**
* Subscribe to a Flux of a patient that has been updated.
*
* #param id
* #return
*/
#GetMapping(value = "/{id}/alerts", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<Message<String>> getPatientAlerts(#PathVariable Long id) {
Flux<Message<String>> messageFlux = Flux.from(pollableReactiveFlow);
return messageFlux;
}
#GetMapping(value = "/generate")
public void generateJmsMessage() {
for (long i = 0L; i < 100; i++) {
Patient patient = new Patient();
patient.setId(i);
send(patient);
System.out.println("Message was sent to the Queue");
}
}
void send(Patient patient) {
this.jmsTemplate.convertAndSend(this.patientQueue, patient);
}
}
If anyone can tell me why the messages do not get sent to the client until after the server is killed, I would appreciate it.
Works well for me:
#SpringBootApplication
#RestController
public class SpringIntegrationSseDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringIntegrationSseDemoApplication.class, args);
}
#Autowired
private ConnectionFactory connectionFactory;
#Autowired
private JmsTemplate jmsTemplate;
#Bean
public Publisher<Message<String>> jmsReactiveSource() {
return IntegrationFlows
.from(Jms.messageDrivenChannelAdapter(this.connectionFactory)
.destination("testQueue"))
.channel(MessageChannels.queue())
.log(LoggingHandler.Level.DEBUG)
.log()
.toReactivePublisher();
}
#GetMapping(value = "/events", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> getPatientAlerts() {
return Flux.from(jmsReactiveSource())
.map(Message::getPayload);
}
#GetMapping(value = "/generate")
public void generateJmsMessage() {
for (int i = 0; i < 100; i++) {
this.jmsTemplate.convertAndSend("testQueue", "testMessage #" + (i + 1));
}
}
}
In one terminal I have curl http://localhost:8080/events which waits for SSEs from that Flux.
In other terminal I perform curl http://localhost:8080/generate and see in the first one:
data:testMessage #1
data:testMessage #2
data:testMessage #3
data:testMessage #4
I use Spring Boot 2.0.0.BUILD-SNAPSHOT.
Also see here: https://spring.io/blog/2017/03/08/spring-tips-server-sent-events-sse

Categories