Java rest api that needs to wait before processing - java

I have a Java rest API which will be used by IOT devices to send data. Each device has a time period (say 15 seconds) to communicate with the API. Within that time period there can be more than one message with the same set of data.
What I want to do is, when the API receive a new message from a device, it wait till the end of the time period and collect the messages received. And process the messages only when the time period is over.
What should I use to collect and process messages for a given time period?
Thanks.
EDIT
Using spring boot.

You should try using an asyncronous endpoint to call to a syncronous REST. You can define what to do after a timeout is reached.
For example, in Spring Boot you could return a Callable and use a TaskExecutor:
#Controller
public class MyController {
#RequestMapping("/endpoint")
public #ResponseBody WebAsyncTask<String> handleRequest (HttpServletRequest request) {
Callable<String> callable = () -> {
return "Callable";
};
ConcurrentTaskExecutor taskExecutor = new ConcurrentTaskExecutor(Executors.newFixedThreadPool(1));
return new WebAsyncTask<>(15000L, taskExecutor, callable);
}
}
You will probably need to add some configuration in Spring for the Task Executor Thread Pool:
#SpringBootApplication
public class AsyncConfigExample {
#Bean
WebMvcConfigurer configurer(){
return new WebMvcConfigurerAdapter(){
#Override
public void configureAsyncSupport (AsyncSupportConfigurer configurer) {
ThreadPoolTaskExecutor t = new ThreadPoolTaskExecutor();
t.setCorePoolSize(10);
t.setMaxPoolSize(100);
t.setQueueCapacity(50);
t.setAllowCoreThreadTimeOut(true);
t.setKeepAliveSeconds(120);
t.initialize();
configurer.setTaskExecutor(t);
}
};
}
}
Here is more to read:
Spring MVC: Configuring Asynchronous Request Processing
Spring Boot: Executing asynchronous method backed with a queue

Related

Spring Integration: How to send messages from pubsub subscribers to external systems/servers with Http Methods

I have been trying to send messages to external systems(using rest template POST, PUT etc) from the service activators as below.
Below is my pubsub consumer class
public class MyConsumer{
#Autowired
ExternalService externalService;
#Bean
public PubSubInboundChannelAdapter messageChannelAdapter(final #Qualifier("myInputChannel") MessageChannel inputChannel,
PubSubTemplate pubSubTemplate)
{
PubSubInboundChannelAdapter adapter = new PubSubInboundChannelAdapter(pubSubTemplate, pubSubSubscriptionName);
adapter.setOutputChannel(inputChannel);
adapter.setAckMode(AckMode.AUTO_ACK);
adapter.setErrorChannelName("pubsubErrors");
return adapter;
}
#ServiceActivator(inputChannel = "pubsubErrors")
public void pubsubErrorHandler(Message<MessagingException> exceptionMessage) {
BasicAcknowledgeablePubsubMessage originalMessage = (BasicAcknowledgeablePubsubMessage) exceptionMessage
.getPayload().getFailedMessage().getHeaders().get(GcpPubSubHeaders.ORIGINAL_MESSAGE);
originalMessage.nack();
}
#Bean
public MessageChannel myInputChannel() {
return new DirectChannel();
}
#Bean
#ServiceActivator(inputChannel = "myInputChannel")
public MessageHandler messageReceiver_AddCustomer() {
return message -> {
externalService.postDataTOExternalSystems(new String((byte[]) message.getPayload());
};
}
#Bean
#ServiceActivator(inputChannel = "myInputChannel")
public MessageHandler messageReceiver_DeleteCustomer() {
return message -> {
externalService.deleteCustomer(new String((byte[]) message.getPayload());
BasicAcknowledgeablePubsubMessage originalMessage =
message.getHeaders().get(GcpPubSubHeaders.ORIGINAL_MESSAGE, BasicAcknowledgeablePubsubMessage.class);
originalMessage.ack();
};
}
}
ExternalService below is the service which sends data to the external systems.
public class ExternalService{
void postDataTOExternalSystems(Object obj){
// RequestEntity object formed with HttpEntity object using obj(in json) and headers
restTemplate.exchange("https://externalsystems/",HttpMethod.POST,requestEntity,Object.class);
}
void deleteDatafromExternalSystems(Object obj){
// RequestEntity object formed with HttpEntity object using obj(in json) and headers
restTemplate.exchange("https://externalsystems/",HttpMethod.Detele,requestEntity,Object.class);
}
}
Since both the methods messageReceiver_AddCustomer and messageReceiver_deleteCustomer are using same channel whats happening is when I try to just addcustomer, the deleteCustomer is also called by default.
I was thinking of creating a seperate channel for deleteCustomer, but creating in this way leads to creating channels for every usecase.
Hence would like to know three things here.
Is there is any other approach of sending through Spring integration through which I can send data to external systems using a single Channel or a different utilization of Channels.
If any error in the external service calls leads to unending of failure logs in the console
message_id: "6830962001745961"
publish_time {
seconds: 1675783352
nanos: 547000000
}
}, timestamp=1675783353720}]': error occurred in message handler
It's not clear what is your expectation for such a logic. You have two contradicting subscribers for the same input channel. It sounds more like you need a router first to determine where to proceed next with an input message from Pub/Sub: https://docs.spring.io/spring-integration/reference/html/message-routing.html#messaging-routing-chapter.
but creating in this way leads to creating channels for every usecase.
Sure! You can go without Spring Integration and just do everything with the plain if..else. So, what's a difference? Therefore I don't see a reasonable argument in your statement. Yo have different HTTP methods, and that is OK to map them via Spring Integration router to respective sub-flows. The MessageChannel is really first-class citizen in Spring Integration. It makes a solution as loosely-coupled as possible.
Several subscribers on your current DirectChannel will lead to a round-robin logic by default, so you'll be surprised that one message from Pub/Sub creates a customer, another deletes and so on in rounds. The PublishSubscribeChannel will make it bad as well: both of your subscribers are going to be called, so created first, then deleted immediately.

Getting data back from #Async function call

Hi I am new to multithreading in java. Can someone please help me with this:
My service:
#Async
public List<String> doSomething(int a){
//Do something
return list;
}
SpringbootApplication:
#SpringBootApplication
#EnableAsync
public class Test {
public static void main(String[] args) {
SpringApplication.run(Test.class, args);
}
}
Async config:
#Configuration
#EnableAsync
public class AsyncConfig {
#Bean(name ="taskExecutor")
public Executor taskExecutor(){
ThreadPoolTaskExecutor executor=new ThreadPoolTaskExecutor();
executor.setCorePoolSize(2);
executor.setMaxPoolSize(2);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("userThread-");
executor.initialize();
return executor;
}
}
Controller:
#RestController
public class Controller{
#Autowired
private Service service;
#GetMapping("test")
public List<String> getAll(){
return service.doSomething(1);
}
}
When I hit this get request from postman it is showing up blank in the response. I understand that my call is going asynchronously and the response is coming back even before the my method is called. Is there any way to see this response by changing some settings in either my postman or spring boot application
If you want to process the request asynchronously but also want the API client to receive the response after it finishes processing such that from the client 's point of view , the request processing still looks like synchronous , the key is to use the Servlet3 asynchronous processing feature.
You do not need to configure it to execute asynchronously in the service level using #Aysnc. Instead configure the controller method to return CompletableFuture. Under the cover , it will trigger Servlet3 's asynchronous request processing which will process the request in another thread besides the HTTP thread that receive the request.
So your codes should look something like:
public class Service {
//No need to add #Async
public List<String> doSomething(int a){
return list;
}
}
#RestController
public class Controller{
#Autowired
private Service service;
#GetMapping("test")
public CompletableFuture<List<String>> getAll(){
return CompletableFuture.supplyAsync(()->service.doSomething(1));
}
}
For details about the Servlet3 asynchronous request processing supported in spring-mvc , you can refer to this blog series start from this .
You can return CompletableFuture. You will receive http response when CompleteableFuture will be completed.
Service:
#Async
public CompletableFuture<List<String>> doSomething() {
return CompletableFuture.completedFuture(Arrays.asList("1", "2", "3"));
}
Controller:
#GetMapping("test")
public CompletableFuture<List<String>> getAll(){
return service.getAll();
}
If you want to use async I would split your single request into so called "start task" and "get task result" requests. Your application returns "request id" for "start task" request. Then you use "request id" when performing "get task result". Such a scenario is a common way in the Batch Processing task. If you use Spring, you may be interesting investigating Spring Batch framework, which has Start/Stop/Restart job functionality among others.

How to set a timeout on a Spring Boot REST API?

I have some REST APIs that may take a while to execute, and I want to limit their execution duration. Preferably, if 30 seconds passed and the request didn't return, I would like to return a specific HTTP code / data and terminate that request completly.
The current code:
#RestController
#CrossOrigin(origins = {"*"}, maxAge = 4800, allowCredentials = "false")
public class APIController {
#RequestMapping(value = "/api/myapifunc", method = RequestMethod.POST, produces = "application/json")
public ResponseEntity<?> optimize(#RequestParam(value="param1", defaultValue="")) {
// Code here
}
It looks like you are describing the Circuit Breaker pattern. If you have control over both the client and server code and want to explore Spring Cloud and Netflix Hysterix libraries you can take a look at Getting Started: Circuit Breaker guide.
If you are using Apache Tomcat as your servlet container you can configure Stuck Thread Detection Valve:
This valve allows to detect requests that take a long time to process, which might indicate that the thread that is processing it is stuck. Additionally it can optionally interrupt such threads to try and unblock them.
When such a request is detected, the current stack trace of its thread is written to Tomcat log with a WARN level.
The IDs and names of the stuck threads are available through JMX in the stuckThreadIds and stuckThreadNames attributes. The IDs can be used with the standard Threading JVM MBean (java.lang:type=Threading) to retrieve other information about each stuck thread.
With Spring Boot 2.3 / Tomcat 9, you can set a timeout for ALL incoming HTTP requests to complete by installing a Tomcat StuckThreadDetectionValve. Here's the Spring configuration code you'll need (it's Kotlin):
import org.apache.catalina.valves.StuckThreadDetectionValve
import org.springframework.beans.factory.annotation.Value
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory
import org.springframework.boot.web.server.WebServerFactoryCustomizer
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
#Configuration
class RequestTimeoutConfiguration(
#Value("\${app.tomcat.stuck-thread-detection.request-timeout.seconds}")
private val stuckThreadTimeoutSeconds: Int
) {
#Bean
fun stuckThreadDetectionValve() =
StuckThreadDetectionValve().apply {
threshold = stuckThreadTimeoutSeconds
interruptThreadThreshold = stuckThreadTimeoutSeconds
}
#Bean
fun stuckThreadDetectionWebServerFactoryCustomizer(valve: StuckThreadDetectionValve) =
WebServerFactoryCustomizer { factory: TomcatServletWebServerFactory ->
factory.addContextValves(valve)
}
}
Then you just need the property in application.properties to control it:
app.tomcat.stuck-thread-detection.request-timeout.seconds=130
#RequestMapping(value = "/api/myapifunc", method = RequestMethod.POST, produces =
"application/json")
public ResponseEntity<?> optimize(#RequestParam(value="param1", defaultValue="")) {
return new Callable<String>() {
#Override
public String call() throws Exception {
Thread.sleep(3000); //this will cause a timeout
return "foobar";
}
};
}
Future you can use or annotation
#Timed
#Transactional(timeout = 3000)
You can use Future timeout:
final Future<Object> submit = service.submit(new Callable<Object>() {
#Override
public Object call() throws Exception {
......YOUR CODE
return "";
}
});
try {
submit.get(3, TimeUnit.SECONDS);
} catch (Exception e) {
log.error("fail",e);
}
You can set this property configuration
server.connection-timeout=30000
in your application.properties.
Based on official documentation says:
server.connection-timeout= # Time that connectors wait for another HTTP request before closing the connection. When not set, the connector's container-specific default is used. Use a value of -1 to indicate no (that is, an infinite) timeout.

Returning a value from direct channel flow and proceeding with async flow in Spring Integration

I have the following integration configuration in my web app:
#Bean
IntegrationFlow giraFlow() {
return IntegrationFlows.from(
MessageChannels.direct("gira.input"))
.split()
.transform(transformer)
.handle(parserService)
.channel(routerChannel())
.get();
}
#Bean
MessageChannel routerChannel() {
return MessageChannels.queue("routerChannel", 10)
.get();
}
#Bean
IntegrationFlow routerChannelFlow() {
return IntegrationFlows.from(
routerChannel())
.route(p -> p.getKind().name(),
m -> m.suffix("Channel")
.channelMapping(TaskKind.CREATE.name(), "create")
.channelMapping(TaskKind.RELOAD.name(), "reload")
.get();
}
and a gateway:
#MessagingGateway
public interface GW {
#Gateway(requestChannel = "gira.input")
Task gira(Collection<Request> messages);
}
and a parserService
#Service
#Slf4j
public class ParserService {
public Task handle(IssueTrackerTask task) {
log.info("Parser service handling task {}", task);
return task;
}
}
I call the gateway method from Spring MVC controller and I want it to return me a Task object that the parserService returns in it's body method. The important thing is that I want controller to be blocked until it gets the value from the parserService. After it gets this value, I want my integration flow to proceed asynchronously with the routerChannelFlow, so that web controller method would return as fast as possible, and all heavy operations in the routerChannelFlow would be done without blocking controller.
Here's a part of the controller that has this gateway method call:
...
Task gira = gw.gira(messages);
log.info("Result {}", gira);
...
The problem is that log.info is never reached and gira() gateway is blocked forever.
How can I achieve my desired behavior?
P.S. The parserService is actually not needed in my app, this is just what I thought would help me to define a return value for my gateway, but it actually did not help:(
UPDATE
So here's what I got after Gary Russell's comment:
#Bean
public ThreadPoolTaskExecutor executor() {
ThreadPoolTaskExecutor pool = new ThreadPoolTaskExecutor();
pool.setCorePoolSize(10);
pool.setMaxPoolSize(10);
pool.setWaitForTasksToCompleteOnShutdown(true);
return pool;
}
#Bean
MessageChannel routerChannel() {
return MessageChannels
.publishSubscribe("routerChannel", executor())
.get();
}
#Bean
IntegrationFlow routerChannelFlow() {
return IntegrationFlows
.from(routerChannel())
.publishSubscribeChannel(s -> s
.subscribe(f -> f
.bridge(null))
.subscribe(process()))
.get();
}
#Bean
IntegrationFlow process() {
return f ->
f.<IssueTrackerTask, String>route(p -> p.getKind().name(),
m -> m.suffix("Channel")
.channelMapping(TaskKind.CREATE.name(), "create")
.channelMapping(TaskKind.RELOAD.name(), "reload")
}
And when I try to use this pipeline, I get the following error Dispatcher has no subscribers for channel 'application:development:9000.dummy'. It's definitely a misconfiguration issue, but I cannot figure out what am I doing wrong.
UPDATE
Changed channel("dummy") to bridge(null).
What is downstream of the create and reload channels?
What do you do with the Task result in the controller (aside from logging it)?
If you don't need a result, change the gateway return to void and add an executor channel downstream of the gateway.
If you want the Task object returned, you need the routerChannel to be a publish/subscribe channel with an executor and two subscribers - a bridge to nowhere (input channel and no output channel) which will return the Task to the gateway, and the router, which will route the Task on a separate thread; the downstream flows from the router must not return a result (the gateway will have long-before stopped waiting for a result).
Instead of adding an executor to routerChannel, you could make the two router channels executor channels instead.
Your last solution is almost there, you just don't need to use .channel("dummy"), but just .bridge(null) in that first subflow subscriber for the .publishSubscribeChannel().
What you need there is just to send message back to the gateway's replyChannel, which is TemporaryReplyChannel in message headers.
The BridgeHandler is the best way do "nothing" but just send message to the appropriate replyChannel from headers.
Also consider to add executor to the channel after .split() if your .transform() and .hanlde() logic is enough heavy.

First Request time out then all request served

I am writing spring rest service using spring boot framework and jetty as container.
In controller I have used the callable like this.
#RequestMapping(value = "/{key}/events", method = RequestMethod.GET)
public Callable<String> getEvents(#PathVariable("key") final String key,
#RequestParam(required = false) final String startAt,
#RequestParam(required = false) final String maxResults) {
return new Callable<String>() {
#Override
public String call() throws Exception {
// here logic that return json string
}
}
}
and the servlet conatiner factory I wrote like this
#Bean
public EmbeddedServletContainerFactory servletContainer(){
JettyEmbeddedServletContainerFactory jetty=new JettyEmbeddedServletContainerFactory();
jetty.addServerCustomizers(new JettyServerCustomizer() {
#Override
public void customize(final Server server) {
// Tweak the connection pool used by Jetty to handle incoming HTTP connections
final QueuedThreadPool threadPool = server.getBean(QueuedThreadPool.class);
threadPool.setMaxThreads(Integer.valueOf(200));
threadPool.setMinThreads(Integer.valueOf(100));
threadPool.setIdleTimeout(Integer.valueOf(100000));
threadPool.setStopTimeout(10000);
}
});
jetty.setPort(4040);
jetty.setContextPath("/mycontextpath");
return jetty;
}
Now My problem is when I run application,
I hit the url from browser first time it not gives the output (service not available message from jetty server).
but again I hit the URL second, third, fourth time it gives me the output.
So for implementing Callable as Controller do I missing something?
I debug code and found that first time internal processing is still going on and the browser complete response...
so What I do? please suggest,
My main aim is that controller should accept more client request at particular time and respond.

Categories