How await QueueChannel all message processed? - java

Consider a code:
#Configuration
public class MyConf {
#MessagingGateway(defaultRequestChannel = "channel")
public interface Sender {
void send(String out);
}
}
#Component
public class Consumer {
#ServiceActivator(inputChannel = "channel", poller = #Poller(fixedRate = "100"))
public void handle(String input) throws InterruptedException {
//
}
}
#Component
public class HistoricalTagRunner implements CommandLineRunner {
#Autowired
private Sender sender;
#Override
public void run(String... args) throws Exception {
List<String> input = ...
input.forEach(r -> sender.send(r));
//ok, now all input is send and application exit
//without waiting for message processing
}
}
So all message are sent to consumer, but application exits without wating that all messages are processed
Is there a way to tell spring wait until all messages in "channel" are processed?

The Spring application is really just Java application and it is really not a Spring responsibility to control how your application is going to live. You can take into a service any Java feature to block a main thread until some event happens.
For example in our samples we use a System.in.read() to block main thread:
System.out.println("Hit 'Enter' to terminate");
System.in.read();
ctx.close();
In this case end-user must enter something from the CLI to unblock that thread and exit from the program.
Another way is to wait for some CountDownLatch if you know a number of messages in advance. So, you flow must "count down" thast latch when a message is processed.

Related

Embedded Kafka Spring test executes before embedded Kafka is ready

I have a Spring Boot project that has a Kafka listener that I want to test using Embedded Kafka. I have the Kafka Listener log out the message "record received". Which will only be be logged out if I add a Thread.sleep(1000) to the start of the method.
Test class:
#SpringBootTest
#DirtiesContext
#EnableKafka
#EmbeddedKafka(partitions = 1, topics = { "my-topic" }, ports = 7654)
class KafkaTest {
private static final String TOPIC = "my-topic";
#Autowired
EmbeddedKafkaBroker kafkaBroker;
#Test
void testSendEvent() throws ExecutionException, InterruptedException {
// Thread.sleep(1000); // I wont see the Listener log message unless I add this sleep
Producer<Integer, String> producer = configureProducer();
ProducerRecord<Integer, String> producerRecord = new ProducerRecord<>(TOPIC, "myMessage");
producer.send(producerRecord).get();
producer.close();
}
private Producer<Integer, String> configureProducer() {
Map<String, Object> producerProps = new HashMap<>(KafkaTestUtils.producerProps(kafkaBroker));
return new DefaultKafkaProducerFactory<Integer, String>(producerProps).createProducer();
}
}
I don't want to use the fickle Thread.sleep() The test is obviously executing before some setup processes have completed. I clearly need to wait on something, but I am not sure what nor how to do it.
Using:
Java 11
Spring Boot 2.5.6
JUnit 5
spring-kafka-test 2.7.8
Add an #EventListener bean to the test context and (for example) count down a CountDownLatch when a ConsumerStartedEvent is received; then in the test
assertThat(eventListner.getLatch().await(10, TimeUnit.SECONDS)).isTrue();
See https://docs.spring.io/spring-kafka/docs/current/reference/html/#events
and
https://docs.spring.io/spring-kafka/docs/current/reference/html/#event-consumption
Or add a ConsumerRebalanceListener and wait for partition assignment.
I clearly need to wait on something, but I am not sure what nor how to do it.
You need to use a different method to give Kafka time to process and route the message ...
Look at this line ...
ConsumerRecord<String, String> message = records.poll(500, TimeUnit.MILLISECONDS);
When testing Kafka listeners we always specify a poll delay. This is because your message is given to kafka, which will then process it in another thread. And you need to wait for it.
Here's how it looks in context of the code its used in.
class UserKafkaProducerTest {
#Test
void testWriteToKafka() throws InterruptedException, JsonProcessingException {
// Create a user and write to Kafka
User user = new User("11111", "John", "Wick");
producer.writeToKafka(user);
// Read the message (John Wick user) with a test consumer from Kafka and assert its properties
ConsumerRecord<String, String> message = records.poll(500, TimeUnit.MILLISECONDS);
assertNotNull(message);
assertEquals("11111", message.key());
User result = objectMapper.readValue(message.value(), User.class);
assertNotNull(result);
assertEquals("John", result.getFirstName());
assertEquals("Wick", result.getLastName());
}
}
This is a code piece from this article, which makes stuff clear.
You can use this small library for testing.
All output records will be collected to blocking queue and you can poll them with timout:
#OutputQueue(topic = TOPIC_OUT, partitions = 1)
private BlockingQueue<ConsumerRecord<String, String>> consumerRecords;
#Test
void shouldFilterRecordWithoutHeader() throws ExecutionException, InterruptedException, TimeoutException {
final String messageIn = "hello world";
try (var producer = producer()) {
producer.send(new ProducerRecord<>(TOPIC_IN, messageIn)).get(5, TimeUnit.SECONDS);
}
ConsumerRecord<String, String> record = consumerRecords.poll(5, TimeUnit.SECONDS);
Assertions.assertThat(record).isNotNull();
}

#Async in SpringBoot creating new thread but Controller waits for async call to finish before sending a response back

Context
I've a scenario - I need to expose a rest endpoint and provide a post method , which will be used by a fronend to receive form values (name, email, address).
With these details I need ti call a third party api (that could take upto 10 seconds to respond).
Additionally , I need to store the processed application and the response in DB somewhere to keep a track for monitoring purposes.
Problem
I plan to use Spring #Async functionality for storing details in Queue (the a DB) so that I dont keep the user waiting for response while I do this storing. The #Async seems to be creating new Thread , I can see from the logs but Controller doesn’t send the response back to client (Which is contrary to actual #Async knowledge I have which is not alot) ; So, until the async completes user has to wait for the response.
Is there anything wrong here or missing ?
TIA for your help.
here are some snippets of my classes-
Main
#EnableAsync(proxyTargetClass = true)
#EnableScheduling
#SpringBootApplication
public class CardsApplication {
public static void main(String[] args) {
SpringApplication.run(CardsApplication.class, args);
}
#Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder.build();
}
}
Controller
#Validated
#RequiredArgsConstructor
public class CardEligibilityController {
private final CardEligibilityService cardEligibilityService;
#PostMapping("/check-eligibility")
#CrossOrigin(origins = "*")
public EligibilityResponse checkEligibility(#RequestBody #Valid Applicant applicant){
return cardEligibilityService.eligibilityService(applicant);
}
}
Service 1
public interface CardEligibilityService {
EligibilityResponse eligibilityService(Applicant applicant);
}
#Slf4j
#Service
#RequiredArgsConstructor
public class CardEligibilityServiceImpl implements CardEligibilityService {
private final ThirdPartyEligibilityAdapter thirdPartyEligibilityAdapter;
private final QueueService queueService;
private final QueueMessageResponseService queueMessageResponseService;
#Override
public EligibilityResponse eligibilityService(Applicant applicant){
EligibilityResponse eligibilityResponse = checkEligibility(applicant);
queueService.pushMessage(queueMessageResponseService.createQueueResponse(applicant,eligibilityResponse));
return eligibilityResponse;
}
private EligibilityResponse checkEligibility(Applicant applicant) {
return thirdPartyEligibilityAdapter.getEligibility(applicant);
}
}
Service 2
public interface QueueService {
void pushMessage(QueueMessage queueMessage);
void retry();
}
#Service
#RequiredArgsConstructor
#Slf4j
public class QueueServiceImpl implements QueueService{
private final List<QueueMessage> deadQueue = new LinkedList<>();
//TODO check why async gets response stuck
#Override
#Async
public void pushMessage(QueueMessage queueMessage){
try {
//Push message to a queue - Queue settings Rabbit/Kafka - then this could be
//used by listeners to persist the data into DB
log.info("message queued {} ", queueMessage);
} catch (Exception e) {
log.error("Error {} , queueMessage {} ", e, queueMessage);
deadQueue.add(queueMessage);
}
}
**This method is a fault tolerance mechanism in case push to queue had any issues, The Local Method call to pushMessage isn’t the problem I also tried this by deleting retry method method**
#Override
#Scheduled(fixedDelay = 300000)
public void retry() {
log.info("Retrying Message push if there are any failure in enqueueing ");
final List<QueueMessage> temp = new LinkedList<>(deadQueue);
deadQueue.clear();
Collections.reverse(temp);
temp.forEach(this::pushMessage);
}
}
Service 3
public interface QueueMessageResponseService {
QueueMessage createQueueResponse(Applicant applicant, EligibilityResponse eligibilityResponse);
}
#Service
public class QueueMessageResponseServiceServiceImpl implements QueueMessageResponseService {
#Override
public QueueMessage createQueueResponse(Applicant applicant, EligibilityResponse eligibilityResponse) {
return new QueueMessage(applicant,eligibilityResponse);
}
}
EDIT 2
THE MOST STRANGE BEHAVIOUR
If I add Thread.sleep(20); in my async method, This works as expected , the user gets a response back without waiting for async to complete. But Still unable to understand the cause.
#Async
public void pushMessage(QueueMessage queueMessage) {
try {
//Push message to a queue - Queue settings Rabbit/Kafka - then this could be
//used by listeners to persist the data into DB
Thread.sleep(20);
log.info("message queued {} ", queueMessage);
} catch (Exception e) {
log.error("Error {} , queueMessage {} ", e, queueMessage);
deadQueue.add(queueMessage);
}
}
The call to pushMessage in retry is a LOCAL call. So the proxy is not involved an the method is executed synchronously.
You have to move the async method to it's own class.

How to listen to dynamic destinations using Spring Boot?

We have an application using Spring Boot and its JMS facility. At runtime, we have different producers that jump online and tell our application the name of the topic or queue to listen to. Right now, we have:
#JmsListener(destination = "helloworld.q")
public void receive(String message) {
LOGGER.info("received message='{}'", message);
}
which works when we send a message to the helloworld.q topic. The problem is, we won't know what the name of the topic will be until runtime, and JmsListener seems to want a constant expression.
Message producers will hook into our ActiveMQ instance and broadcast a message telling us we need to start listening to their topic, such as "Wasabi", "WhitePaper", "SatelliteMajor", "BigBoosters", etc. There is no way to know at runtime which topics we'll need to start listening to.
I've read the Spring documentation that explains how to listen to topics/queues at runtime (sort of):
#Configuration
#EnableJms
public class ReceiverConfig implements JmsListenerConfigurer {
#Override
public void configureJmsListeners(JmsListenerEndpointRegistrar registrar) {
SimpleJmsListenerEndpoint endpoint = new SimpleJmsListenerEndpoint();
endpoint.setId("myJmsEndpoint");
endpoint.setDestination("anotherQueue");
endpoint.setMessageListener(message -> {
// processing
});
registrar.registerEndpoint(endpoint);
}
// other methods...
}
I've shoved that into our Receiver config as a test, and it does get called when we send a message. The problem is, Spring makes all this stuff get called automagically and we don't know where and how to give this method the name of the topic/queue the endpoint needs to listen to. Also, the message listener never seems to get called, but that's a separate problem; I'm sure we can solve it if we at least can send the custom topic or queue for it to listen to.
We're using Spring 2.x.
You can use a property placeholder for the destination name
#SpringBootApplication
public class So56226984Application {
public static void main(String[] args) {
SpringApplication.run(So56226984Application.class, args);
}
#JmsListener(destination = "${foo.bar}")
public void listen(String in) {
System.out.println(in);
}
#Bean
public ApplicationRunner runner(JmsTemplate template) {
return args -> template.convertAndSend("baz", "qux");
}
}
Then set the property, e.g. in application.yml for a Spring Boot app, or a command-line property when launching the JVM
-Dfoo.bar=baz
EDIT
You can make the listener bean a prototype and adjust an environment property.
#SpringBootApplication
public class So56226984Application {
public static void main(String[] args) {
SpringApplication.run(So56226984Application.class, args).close();
}
#Bean
public ApplicationRunner runner(JmsTemplate template, JmsListenerEndpointRegistry registry,
ConfigurableApplicationContext context) {
return args -> {
Scanner scanner = new Scanner(System.in);
String queue = scanner.nextLine();
Properties props = new Properties();
context.getEnvironment().getPropertySources().addLast(new PropertiesPropertySource("queues", props));
while (!"quit".equals(queue)) {
System.out.println("Adding " + queue);
props.put("queue.name", queue);
context.getBean("listener", Listener.class);
template.convertAndSend(queue, "qux sent to " + queue);
System.out.println("There are now " + registry.getListenerContainers().size() + " containers");
queue = scanner.nextLine();
}
scanner.close();
};
}
#Bean
#Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public Listener listener() {
return new Listener();
}
public static class Listener {
#JmsListener(destination = "${queue.name}")
public void listen(String in) {
System.out.println(in);
}
}
}

Running a CompletableFuture from an Actor

I'm playing around with reactive patterns in a Java (8) Spring Boot (1.5.2.RELEASE) application with Akka (2.5.1). It's coming along nicely but now I'm stuck trying to run a CompletableFuture from an actor. To simulate this I have created a very simple service that returns a CompletableFuture. However, when I then try to return the result to the calling controller I get errors about dead-letters and no response is returned.
The error I am getting is:
[INFO] [05/05/2017 13:12:25.650] [akka-spring-demo-akka.actor.default-dispatcher-5] [akka://akka-spring-demo/deadLetters] Message [java.lang.String] from Actor[akka://akka-spring-demo/user/$a#-1561144664] to Actor[akka://akka-spring-demo/deadLetters] was not delivered. [1] dead letters encountered. This logging can be turned off or adjusted with configuration settings 'akka.log-dead-letters' and 'akka.log-dead-letters-during-shutdown'.
Here is my code. This is the controller calling the actor:
#Component
#Produces(MediaType.TEXT_PLAIN)
#Path("/")
public class AsyncController {
#Autowired
private ActorSystem system;
private ActorRef getGreetingActorRef() {
ActorRef greeter = system.actorOf(SPRING_EXTENSION_PROVIDER.get(system)
.props("greetingActor"));
return greeter;
}
#GET
#Path("/foo")
public void test(#Suspended AsyncResponse asyncResponse, #QueryParam("echo") String echo) {
ask(getGreetingActorRef(), new Greet(echo), 1000)
.thenApply((greet) -> asyncResponse.resume(Response.ok(greet).build()));
}
}
Here is the service:
#Component
public class GreetingService {
public CompletableFuture<String> greetAsync(String name) {
return CompletableFuture.supplyAsync(() -> "Hello, " + name);
}
}
Then here is the actor receiving the call. At first I had this:
#Component
#Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class GreetingActor extends AbstractActor {
#Autowired
private GreetingService greetingService;
#Autowired
private ActorSystem system;
#Override
public Receive createReceive() {
return receiveBuilder()
.match(Greet.class, this::onGreet)
.build();
}
private void onGreet(Greet greet) {
greetingService.greetAsync(greet.getMessage())
.thenAccept((greetingResponse) -> getSender().tell(greetingResponse, getSelf()));
}
}
This resulted in 2 calls being handled correctly but after that I would get dead-letter errors. Then I read here what was probably causing my problems:
http://doc.akka.io/docs/akka/2.5.1/java/actors.html
Warning
When using future callbacks, inside actors you need to carefully avoid closing over the containing actor’s reference, i.e. do not call methods or access mutable state on the enclosing actor from within the callback. This would break the actor encapsulation and may introduce synchronization bugs and race conditions because the callback will be scheduled concurrently to the enclosing actor. Unfortunately there is not yet a way to detect these illegal accesses at compile time. See also: Actors and shared mutable state
So I figured the idea is that you pipe the result to self() after which you can do getSender().tell(response, getSelf()).
So I altered my code to this:
#Component
#Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class GreetingActor extends AbstractActor {
#Autowired
private GreetingService greetingService;
#Autowired
private ActorSystem system;
#Override
public Receive createReceive() {
return receiveBuilder()
.match(Greet.class, this::onGreet)
.match(String.class, this::onGreetingCompleted)
.build();
}
private void onGreet(Greet greet) {
pipe(greetingService.greetAsync(greet.getMessage()), system.dispatcher()).to(getSelf());
}
private void onGreetingCompleted(String greetingResponse) {
getSender().tell(greetingResponse, getSelf());
}
}
The onGreetingCompleted method is being called with the response from the GreetingService but at that time I again get the dead-letters error so for some reason it can't send the response back to the calling controller.
Note that if I change the service to this:
#Component
public class GreetingService {
public String greet(String name) {
return "Hello, " + name;
}
}
And the onGreet in the actor to:
private void onGreet(Greet greet) {
getSender().tell(greetingService.greet(greet.getMessage()), getSelf());
}
Then everything works fine. So it would appear that I have my basic Java/Spring/Akka set up correctly, it's just when trying to call a CompletableFuture from my actor that the problems start.
Any help would be much appreciated, thanks!
The getSender method is only reliably returning the ref of the sender during the synchronous processing of the message.
In your first case, you have:
greetingService.greetAsync(greet.getMessage())
.thenAccept((greetingResponse) -> getSender().tell(greetingResponse, getSelf()));
Which means that getSender() is invoked async once the future completes. Not reliable anymore. You can change that to:
ActorRef sender = getSender();
greetingService.greetAsync(greet.getMessage())
.thenAccept((greetingResponse) -> sender.tell(greetingResponse, getSelf()));
In your second example, you have
pipe(greetingService.greetAsync(greet.getMessage()), system.dispatcher()).to(getSelf());
You are piping the response to "getSelf()", i.e. your worker actor. The original sender will never get anything (thus the ask expires). You can fix that into:
pipe(greetingService.greetAsync(greet.getMessage()), system.dispatcher()).to(getSender());
In the third case, you have getSender() being executed synchronously during the processing of the message, thus it works.

Spring Async is blocking and prevents nested async calls

Can anybody tell my is there a way of using the Spring Framework's #Async annotation without blocking / waiting on the result? Here is some code to clarify my question:
#Service
public class AsyncServiceA {
#Autowired
private AsyncServiceB asyncServiceB;
#Async
public CompletableFuture<String> a() {
ThreadUtil.silentSleep(1000);
return asyncServiceB.b();
}
}
#Service
public class AsyncServiceB {
#Async
public CompletableFuture<String> b() {
ThreadUtil.silentSleep(1000);
return CompletableFuture.completedFuture("Yeah, I come from another thread.");
}
}
and the configuration:
#SpringBootApplication
#EnableAsync
public class Application implements AsyncConfigurer {
private static final Log LOG = LogFactory.getLog(Application.class);
private static final int THREAD_POOL_SIZE = 1;
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Bean
public CommandLineRunner commandLineRunner(ApplicationContext ctx) {
return args -> {
final AsyncServiceA bean = ctx.getBean(AsyncServiceA.class);
bean.a().whenComplete(LOG::info);
};
}
#Override
#Bean(destroyMethod = "shutdown")
public ThreadPoolTaskExecutor getAsyncExecutor() {
final ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(THREAD_POOL_SIZE);
executor.setMaxPoolSize(THREAD_POOL_SIZE);
executor.initialize();
return executor;
}
#Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
// omitted
}
}
When I run the application the executor goes through calling AsyncServiceA.a() and leaves, but it still holds the thread from the pool waiting on the CompletableFuture.get() method. Since there is just a single thread in the pool the AsyncServiceB.b() cannot be executed. What I'm expecting is that thread to be returned to the pool after it executes the AsyncServiceA.a() and then be available to execute the AsyncServiceB.b().
Is there a way to do that?
Note 1: I've tried with ListenableFuture also but the result is the same.
Note 2: I've successfully did it manually (without the #Async) by giving the executor to each method like so:
AsyncServiceA
public CompletableFuture<String> manualA(Executor executor) {
return CompletableFuture.runAsync(() -> {
LOG.info("manualA() working...");
ThreadUtil.silentSleep(1000);
}, executor)
.thenCompose(x -> asyncServiceB.manualB(executor));
}
AsyncServiceB
public CompletableFuture<String> manualB(Executor executor) {
return CompletableFuture.runAsync(() -> {
LOG.info("manualB() working...");
ThreadUtil.silentSleep(1000);
}, executor)
.thenCompose(x -> CompletableFuture
.supplyAsync(() -> "Yeah, I come from another thread.", executor));
}
Here is the ThreadUtil if someone was wondering.
public class ThreadUtil {
public static void silentSleep(long millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
Update: Added issue for non-blocking Async annotation https://jira.spring.io/browse/SPR-15401
The #Async support has been part of Spring since Spring 3.0 which was way before the existence of Java8 (or 7 for that matter). Although support has been added for CompletableFutures in later versions it still is to be used for simple async execution of a method call. (The initial implementation reflects/shows the call).
For composing callbacks and operate non blocking the async support was never designed or intended to.
For non blocking support you would want to wait for Spring 5 with its reactive/non-blocking core, next to that you can always submit a ticket for non-blocking support in the async support.
I've responded on the ticket https://jira.spring.io/browse/SPR-15401 but I'll respond here as well to qualify the response by M. Deinum.
#Async by virtue of how it works (decorating the method call via AOP) can only do one thing, which is to turn the entire method from sync to async. That means the method has to be sync, not a mix of sync and async.
So ServiceA which does some sleeping and then delegates to the async ServiceB would have to wrap the sleeping part in some #Async ServiceC and then compose on ServiceB and C. That way ServiceA becomes async and does not need to have the #Async annotation itself..

Categories