I have a Spring Rest Controller endpoint:
#PostMapping("/someheavyjob")
public Response indexAllReports() {
someService.startLongRunningOperation();
return Response.ok().build();
}
and then in the service class I have a method which performs many computations (databse read, other API calls etc.) :
public void startLongRunningOperation(){
List<String> involvedIds = otherApi.getAllActiveMembersIds(..);
involvedIds.forEach(id -> {
anotherComputatioMethod();
});
}
I know that this approach is bloking user's request until complete the job. I can solve it, it's in that state just to make it clear.
Quesiton: What I am considering about it is:
There should be only one instance of this heavy method running at a time (method: startLongRunningOperation).
Right now, as this method can be invoked from Rest controller, each api call will start this heavy method in new thread. There are mechanisms to rate limit users requests in java (eg. bucket4j), but wanted to ask you guys, what is the best way to handle this case ? Just one instance of long running task fired from Rest API calling Spring Service (which is and should be stateless) .
Edit 1:
To make it clear - blocking: I mean user have to wait for response unit whole task is finished - it can take minutes.
What I want to achieve is not to make this service method synchronized, but when the request comes, and in that time this long running task is working, then reject that 2nd request.
Related
I have an API endpoint /api1 that will make 2 service calls -
insertIndb(), called on certain inputs
verify(), called on certain inputs
I want to return the response of insertIndb() to the caller when they call /api1 only and not the verify() call. The verify() call if called, the caller need not wait for the response of this call but just get something like response in process. Since the goal of verify() is to update db and not return back anything.
So I was suggested to make this verify call in background. How can I do that?
Summarizing my flow below:
Enduser send POST /api1 with payload entity.
If entity does not exist,
the /api1 will make a insertIndb() call and return the response to the end user as 200.
After this, It will call /verify in background, the enduser need not wait for the response. // How to do this?
If entity exists,
It will only call /verify in background, the enduser need not wait for the response. // How to do this?
Probably return just 200 on your request is submitted.
How to run the above /verify calls in background based on the above flow is my question. Could anyone please help me here?
There are multiple ways to may a non-blocking fire-and-forget call like this.
The simplest, IMO, is to use a separate thread to execute the call using a synchronous operation. Another is to use tooling that supports non-blocking calls.
In addition, there are frameworks that simplify this effort. Camel comes to mind. Of course, there is a fair amount of effort to use the framework itself.
You can achieve this by using #EnableAsync annotation in spring by following way:
#SpringBootApplication
#EnableAsync
public class MyApplication {
public static void main(String[] arg) {
SpringApplication.run(MyApplication.class);
}
}
then you need to mark the emailUserInTheBackground method with #Async annotation.
#Service
class AnotherService {
#Async
public void verify(int userId) {
try {
TimeUnit.SECONDS.sleep(10);
System.out.println("Print from async: "+ Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Now add one more logger after a method call, you'll see getUsers(...) call completing first in a different thread even though the emailService thread is blocked for 10 seconds.
anotherService.verify();
System.out.println("Print from service: "+ Thread.currentThread().getName());
Also, there is CompletableFuture for achieving non blocking coding . You can read more about this in below link
https://www.javatpoint.com/completablefuture-in-java
I am writing a service where I want to expose an endpoint which will call another service and if the service call is successful then I want to send back the result to UI/ calling app.
In parallel before sending back the response, I want to execute/submit a task which should run in background and my call should not be dependent on success or failure of this task.
Before returning the response i want to do the-
executorService.execute(object);
This should not be a blocking call..
Any suggestion
Spring Async methods is the way to go here as was suggested in comments.
Some caveats:
Async methods can have different return types, its true that they can return CompletableFuture but this is in case if you called them from some background process and would like to wait/check for their execution status or perhaps, execute something else when the future is ready. In your case it seems that you want "fire-and-forget" behavior. So you should use void return type for your #Async annotated method.
Make sure that you place #EnableAsync. Under that hood it works in a way that it wraps the bean that has #Async methods with some sort of proxy, so the proxy is actually injected into your service. So #EnableAsync turns on this proxy generation mechanism. You can verify that this is the case usually in the debugger and checking the actual type of the injected reference object.
Consider customizing the the task executor to make sure that you're running the async methods with executor that matches your needs. For example, you won't probably want that every invocation of async method would spawn a new thread (and there is an executor that behaves like this). You can read about various executors here for example
Update
Code-wise you should do something like this:
public class MyAsyncHandler {
#Async
public void doAsyncJob(...) {
...
}
}
#Service
public class MyService {
#Autowired // or autowired constructor
private MyAsyncHandler asyncHandler;
public Result doMyMainJob(params) {
dao.saveInDB();
// do other synchronous stuff
Result res = prepareResult();
asyncHandler.doAsyncJob(); // this returns immediately
return res;
}
}
How to implement one-way operation in Web Services (using Java or Spring annotations)?
I have tried to add one way as given below
#WebService
public interface DanduServices {
#Oneway
public void saveDanduInformation(#WebParam(name = "serv") ServDTO Serv, #WebParam(name = "dandu") DanduDTO danduDto);
but it is still request-response not asynchronus or one way.
Could anyone suggest to make a operation one-way in service endpoint and let other operations behave as per request-response?
You need to think in terms of the protocol as well though. In HTTP when you send a request you wait for a response, if no response comes back after an amount of time then you will receive a time-out error. So when you talk about one-way (you should rather say async request maybe) you really need to specify exactly what you mean. Do you want to have confirmation that your message was received i.e. have the server respond back with an OK status code and go off and complete it's task but you not wait for the task to be completed? Then you would need to spawn another thread. Spring has AOP for this the same way it has for transactions with #Transactional. Instead you annotated your method with #Async and return a Future<Something>. You'll also need #EnableAsync in your config. Refer to this article for an example Hot To Do #Async
If you don't even care about if the server received your request you don't want to use TCP/HTTP but instead UDP which is used in VOIP (phone over internet) for instance and is quicker, but it will depend on your client.
I'm experiencing some issues using DeferredResult with Spring, I think I misunderstand something about them.
These DeferredResult are used for long polling.
I have an application on which multiple users can be logged in, and which displays a list of items from a database. The same items are visible for all the connected users. Any user can, at a given time, select one of these items and interact with it, but then the other users cannot interact with this specific item anymore (say like an item is "locked" for the others as soon as someone select it until the user "releases" it).
Anyway, I'm using long polling to notify the others when one user "takes" an item, that this is item is now "locked", to update their interface.
Say I have for example a url for the polling like:
/polling and another like : /take/{itemId}
I have a web application using Spring MVC with the "old-style" XML configuration with the parameters for asynchronous processes:
<task:annotation-driven> in my servlet config, and more importantly (I think) <async-supported>true</async-supported> in the web.xml.
When a user calls /polling, the request is returning a DeferredResult:
private final BlockingQueue<DeferredResult<List<MyItem>>> pendingRequests = new LinkedBlockingQueue<>();
#RequestMapping("/polling")
public DeferredResult<List<MyItem>> poll(...) {
final DeferredResult<List<MyItem>> def = new DeferredResult<>(60000l);
// ... deferred result init like set onCompletion(), onTimeout()...
// add the deferred to a queue
pendingRequests.add(def);
return def;
}
Then when a user "takes" an item and calls /take/{itemId}, something like this:
#RequestMapping("/take/{itemId}")
public void takeItem(...) {
// ..marking the item as taken and saving it into DB..
// and then, notifying the other pending requests
// the item has been taken by someone
List<MyItem> updatedItemsList = getLastItemsFromDb();
for (DeferredResult<...> d : pendingRequests) {
d.setResult(updatedItemsList);
}
}
Note that in the updatedItemsList list, the specific is now marked as "taken".
So here it is, this seems to work fine that way. Immediately after setting result on each items, the corresponding request resumes and "breaks" the long polling process without waiting for the request timeout so that the front javascript can update the list and make a new long request.
The problem is that I recently tried to "convert" this web application by using Spring Boot, and all the "Java/annotation-based" configuration.
And this behavior does not work anymore. Indeed, it's like setting the DeferredResult (in the for loop in the 2nd request) does not trigger the requests to resume anymore, and it has to wait for the timeout to return the result.
However, I found that calling the setResult method on the DeferredResult objects in a TaskExecutor for example, makes everything work like before.
My question is, why ? Why does this work fine with the XML-style configuration, without any executor or explicit background setResult() call, and this does not work anymore with the Java based configuration ?
Did I miss something ?
FYI, the Java configuration is rather "classic", I set #EnableAutoConfiguration, #EnableAsync, extends from WebMvcConfigurerAdapter.
Thanks in advance for reading this and taking time to reply !
I have a situation wherein I call a method which in turn triggers a asynchronous HTTP REST call(sends the status later to another endpoint) before it proceeds further. I want the method to wait until i get the response back to the endpoint, check the status i got and proceed further. I am looking for a feasible solution in Java. Any pseudo code or implementation will be helpful
saw similar case # Lightweight way of waiting for a group of asynchronous Java calls but not much idea about the same whether it is easy to implement.
Implementation details
I have JAX-RS endpoint to handle the async response as below
#POST
#Path("/status")
#Consumes("application/xml")
public Response processConfigStatus(#Context UriInfo uriInfo, ConfigStatus configStatus)
{
// process Status got from the async REST call
return ResponseHelper.createNoContentResponse();
}
Class which handles and processes
Class ProcessData{
public void updateData()
checktStatus(uri);
update();// should wait untill the processConfigStatus() endpoint gives status
}
private checktStatus(String uri){
// makes a REST call to a URI using a JAX-RS webclient or a httpclient this returns HTTP 200 or 204 code immediatley. And then the Server process the request asynchronously and gives back the status to JAX-RS endpoint(/status).
post(uri);
}
}
Method call from another Class
ProcessData pd = new ProcessData()
pd.updateData();
How about using a CountDownLatch?
A synchronization aid that allows one or more threads to wait until a set of operations being performed in other threads completes.
Just as in the link you provided, you'll have to implement a way to simply keep track of how many async calls are stilling waiting for a response and wait until that count is zero.
count = numAsyncCalls
..calling all of the RESTful services here (each call must have some sort of callback to decrement 'count' variable above..
while (count > 0)
wait around
The use of the CountDownLatch in your link looks pretty much the same as my pseudo-code