Spring #Async vs Spring WebFlux - java

I'm trying to convert the following method using WebFlux to avoid using #Async on the same.
#Async
#Override
public void notifyCallback(NotifyCallbackRequest request, String id) {
startProcess(id);
}
Controller class:
#ResponseStatus(HttpStatus.OK)
#PostMapping("/notify-status/{id}")
public void notifyCallback(#PathVariable("id") String id, #RequestBody NotifyCallbackRequest request) {
identityService.notifyCallback(request, id);
}
startProcess(id) is a method that makes an api call via webflux and returns a Mono type.
I am interested that the caller immediately receives a ResponseStatus (HttpStatus.OK) despite what happens next.

You can just subscribe to your "async" task independently of your main reactive chain to start to make it run in the background as soon as the endpoint is called.
#Override
public void notifyCallback(NotifyCallbackRequest request, String id) {
startProcess(id).subscribe();
}
#ResponseStatus(HttpStatus.OK)
#PostMapping("/notify-status/{id}")
public Mono<Void> notifyCallback(#PathVariable("id") String id, #RequestBody NotifyCallbackRequest request) {
identityService.notifyCallback(request, id);
return Mono.empty();
}
You can demo this behaviour using the following endpoint example. If you call the endpoint, it will a 200 straight away and 10 seconds later it will log to your console
#RequestMapping(path = "/async", method = RequestMethod.GET)
public Mono<Void> start() {
Mono.delay(Duration.ofSeconds(10))
.doOnNext(i -> LOGGER.info("we have waited 10 seconds"))
.subscribe();
return Mono.empty();
}

Related

Is there a way to get request URI in Spring?

Whenever a request is made, I need to get the request URI for some internal calculations.
For some time I've been doing it like this:
public Mono<Response> example(ServerHttpRequest req) { ... }
And then using req.getURI(), but that becomes a pain once you need to pass it down multiple times. I need the URI object to extract scheme, schemeSpecificPart, host, port from it.
Is there a way to get these properties without extracting them from a request?
UPD: I see that for Web MVC there are convenient methods to retrieve request URI. But I need the same for reactive stack (netty).
It can be achieved by creating WebFilter that puts ServerHttpRequest into the Context:
#Component
#ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
public class ReactiveRequestContextFilter implements WebFilter {
#Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
return chain
.filter(exchange)
.contextWrite(ctx -> ctx.put(ReactiveRequestContextHolder.CONTEXT_KEY, request));
}
}
Additionally, create a class that provides static access to request data:
public class ReactiveRequestContextHolder {
public static final Class<ServerHttpRequest> CONTEXT_KEY = ServerHttpRequest.class;
public static Mono<ServerHttpRequest> getRequest() {
return Mono.deferContextual(Mono::just).map(ctx -> ctx.get(CONTEXT_KEY));
}
public static Mono<URI> getURI() {
return getRequest().map(HttpRequest::getURI);
}
}
Methods can be accessed through the class name directly without having to instantiate them. Just be aware that it should not be accessed before the filter is executed.
Example of usage:
#RestController
#RequestMapping
public class TestController {
#GetMapping("/test")
public Mono<URI> test() {
return ReactiveRequestContextHolder.getURI();
}
}
Reference
You can try this :
public Mono<Response> example(WebRequest request){
System.out.println(request.getDescription(false));
.......
}
You can turn this false to true in getDescription as false will only give you the Uri which i think is the only thing you need.
You can inject it in any bean.
#Autowired
private HttpServletRequest request;

How to test Controller that returns Mono<ResponseEntity<Void>>?

I'm pretty new to webflux and I am struggling to understand how to test this Controller function.
public Mono<ResponseEntity<Void>> functionName(final Request request) {
RequestDto Dto = RequestMapper.
INSTANCE.toDto(request);
service.functionName(Dto);
return Mono.just(new ResponseEntity<Void>(HttpStatus.OK));
}
You could use WebTestClient that provides fluent API for verifying responses. Here is a short example
#WebFluxTest
class ControllerTest {
#Autowired
private WebTestClient client;
#BeforeEach
void setUp(ApplicationContext context) {
client = WebTestClient.bindToApplicationContext(context).build();
}
#Test
void test() {
client.get()
.uri("/test")
.exchange()
.expectStatus().isOk()
.expectBody().isEmpty();
}
}
In addition, pay attention to endpoint implementation. In reactive you need to build the flow and Webflux will subscribe to it for every request. In case, service.functionName is blocking (non-reactive), consider running it on a separate Scheduler using .subscribeOn(Schedulers.boundedElastic()). For details, check How Do I Wrap a Synchronous, Blocking Call?.
If the Callable resolves to null, the resulting Mono completes empty and we could return 404 applying switchIfEmpty operator.
#GetMapping(path = "/test")
public Mono<ResponseEntity<Void>> functionName(Request request) {
return Mono.fromCallable(() -> {
RequestDto Dto = RequestMapper.INSTANCE.toDto(request);
return service.functionName(Dto);
}).subscribeOn(Schedulers.boundedElastic())
.map(res -> new ResponseEntity<>(HttpStatus.OK))
.switchIfEmpty(Mono.just(new ResponseEntity<>(HttpStatus.NOT_FOUND)));
}

Call Scheduler after method

I have an app, which connects to an API every day and fetches a data (schedulers run every 24h). I would like to add such a functionality:
after user registration call schedulers and force fetching data right now. Could you recommend the best approach in Spring?
#Component
public class GetMyFeeEstimateScheduler extends Scheduler {
#Scheduled(fixedDelay = DELAY)
#Transactional
public void fetchGetMyFeeEstimate() throws Exception {
fetchData();
}
#PostMapping("/signup")
public void signUp(#RequestBody SignUpRequest signUpRequest) {
// ...
// CALL_SCHEDULERS
}
I would prefer to expose the fetchData() as a public method in a service class and then call from the controller and from scheduler.
Another option is to call fetchGetMyFeeEstimate directly from the controller.

Moving common code between two implementations of interface

I have an interface called processor:
public interface Processor {
MyResponse process(Request request, String id);
}
I have two implementations of this processor, one synchronous, the other asynchronous (both Spring Service classes):
This is Sync Version, completes the job and give a fully populated response:
#Service
public class SynchrnousProcess implements Processor {
#Override
public MyResponse process(Request request, String id) {
Job job = getJob(request);
JobParameters parameter = buildParams(request, id);
JobExecution jobExecution = kickOfJob(job, request, parameter);
return buildResponse(request, trackingId, jobExecution);
}
}
This is Async version, which adds requests to blocking queue:
#Service
public class AsyncProcess implements Processor {
private BlockingQueue<Pair> requestQueue = new ArrayBlockingQueue<>(100);
#Override
public MyResponse process(Request request, String id) {
//add request to requestQueue
}
public void completeProcess() {
//take data from queue and process
log.info("start process !!!!!!!!!!!!!!!");
if (!CollectionUtils.isEmpty(requestQueue)) {
requestQueue.stream().forEach(pair -> {
String trackingId = String.valueOf(pair.getFirst());
FeedDto feedDto = (FeedDto) pair.getSecond();
Request request = feedDto.getRequest();
Job job = getJob(request);
JobParameters parameter = getJobParameters(request, trackingId);
JobExecution jobExecution = runJob(job, request, parameter);
}
}
As you can see, completeProcess() takes data out of the queue and processes the job. Common code to do with getting job, building param is duplicated between synchronous run and async.
Another thread runs completeProcess() in background.
I want to achieve a clean design and move common code that are shared by the two interface implementations in one place. How can I achieve this for what I am trying to achieve? It will be really helpful to see an example of the design pattern to use in this case?
In Addition, any suggestion as to where the ExecutorService to process the queued requests should be initiated and how the threads to process these should be started will help a lot.
You could design the interface as it has to perform async operations. For example:
public interface Callback() {
void onResponse(MyResponse mr);
}
public void Processor {
void process(Request request, String id, Callback callback);
}
#Service
public class SynchrnousProcess implements Processor {
#Override
public void process(Request request, String id, Callback callback) {
Job job = getJob(request);
JobParameters parameter = buildParams(request, id);
JobExecution jobExecution = kickOfJob(job, request, parameter);
MyResponse r = buildResponse(request, trackingId, jobExecution);
callback.onResponse(r);
}
}
#Service
public class AsyncProcess implements Processor {
private BlockingQueue<Pair> requestQueue = new ArrayBlockingQueue<>(100);
#Override
public void process(Request request, String id, Callback callback) {
//add request and callback to requestQueue
}
}
I don't know who will process the queue, but surely who will use Processor does not have to worry if the call is synchronous or not.
this is the most similar approach to the one you would use in C# with async await.
In C# you have to return a Task < MyResponse > even in the synchronous case. Then the consumer will always think that the call will be asynchronous.

Spring Boot runs filter twice for requests returning a CompletionStage

I was having an issue where my filter was running twice when methods were returning a CompletionStage. From the documentation on RequestMapping (here), it is a supported return value.
A CompletionStage (implemented by CompletableFuture for example) which the application uses to produce a return value in a separate thread of its own choosing, as an alternative to returning a Callable.
Since the project was pretty complex with a lot of concurrent code, I created a new simple spring-boot project. This is the (only) controller in it:
#Controller
public class BaseController {
#RequestMapping("/hello")
#ResponseBody
public CompletionStage<String> world() {
return CompletableFuture.supplyAsync(() -> "Hello World");
}
}
And a filter:
#WebFilter
#Component
public class GenericLoggingFilter extends GenericFilterBean {
#Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
System.out.println(httpServletRequest.getMethod() + " " +
httpServletRequest.getRequestURI());
chain.doFilter(request, response);
}
}
When I make a call curl http://localhost:8080/hello, it prints GET /hello twice on the console. When I change the controller method to return a String:
#RequestMapping("/hello")
#ResponseBody
public String world() {
return "Hello World";
}
It prints it only once. This behavior is exhibited even if I change it to a Callable, which has no real concurrency significance (of course, spring itself might be treating this now as an Async request).
So, if spring is running the entire web-stack again to have a request context available, even that doesn't really make sense because the following:
#RequestMapping("/hello")
#ResponseBody
public CompletionStage<String> world() {
return CompletableFuture.supplyAsync(() -> {
System.out.println(RequestContextHolder.currentRequestAttributes());
return "Hello World";
});
}
throws an exception: IllegalStateException: No thread-bound request found...
What is surprising is, that the following works:
#RequestMapping("/hello")
#ResponseBody
public Callable<String> world() {
return () -> {
System.out.println(RequestContextHolder.currentRequestAttributes());
return "Hello World";
};
}
So, I am unsure about quite a few things.
It appears that Callable and CompletionStage is being treated differently in context of which thread it is executed in.
If that is the case, then why is my filter running twice in each case? If the job of a filter is to setup a certain request-specific context, it doesn't make sense to run it again for a CompletionStage if it anyways is not accessible.
Why exactly is the filter running twice either which ways?
Please replace GenericFilterBean with OncePerRequestFilter.

Categories

Resources