How to make endpoints initialization asynchronous in Spring Boot? - java

In my Spring Boot project, a couple of REST API endpoints require a class whose initialization takes several minutes. Because of this, it takes several minutes to start the REST API.
Is it possible (using Spring Boot) to make so that these few endpoints are initialized asynchronously i.e. all other endpoints are initialized right away and REST API starts working and these endpoints are initialized whenever the class that they need is initialized and are simply not available to the user before that?
I tried looking into #Async and other ways to make things asynchronous in Spring Boot, but that did not help.
I would really appreciate some help.

Try #Lazy annotation. When it's applied to the spring component, it will be initialized on the first call.
Some resources:
https://www.baeldung.com/spring-lazy-annotation
Java Doc

There's nothing built into Spring to do what you want, but you could implement it yourself by returning a 404 Not Found response while the service is initializing and a 200 OK once it's available. The following is one way to implement that:
#RestController
class ExampleController {
private final Future<SlowInitializationService> service;
ExampleController() {
this.service = ForkJoinPool.commonPool().submit(SlowInitializationService::new);
}
#GetMapping
ResponseEntity<Result> example() throws InterruptedException, ExecutionException {
if (this.service.isDone()) {
return new ResponseEntity<>(this.service.get().perform(), HttpStatus.OK);
}
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
}
Rather than using the common pool, you may want to inject an Executor or similar. It'll depend on the rest of your app and how it handles threading.

Related

Multiple REST Calls using Spring Boot

I'm trying to make 500+ REST calls (POST) to an API using Spring Boot. Currently I'm using a thread pool using callable - executor service since I require the response from the POST call as well. Is there a more efficient way to do this within Spring Boot?
edit - This is a IO Intensive Task
You can simply use WebClient, as it's non-blocking by design.
see e.g.: https://newbedev.com/springboot-how-to-use-webclient-instead-of-resttemplate-for-performing-non-blocking-and-asynchronous-calls
But there are lots of other resources on the web.
However... if you're using RestTemplate:
#Service
public class AsyncService {
private final RestTemplate restTemplate;
public AsyncService(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
#Async
public CompletableFuture<ResponseDto[]> callAsync(RequestDto requestDto) {
ResponseDto[] responseDtos = restTemplate.postForObject("someUrl", requestDto, ResponseDto[].class);
return CompletableFuture.completedFuture(responseDtos);
}
}
Then you can simply loop though all requests from whatever place is ideal for your context using standard Java Future mechanisms.
Just make sure to add #EnableAsync to your application
A more detailed tutorial can be found here: https://spring.io/guides/gs/async-method/

Micrometer Prometheus metrics in jersey application (Non spring)

I have a web application (war) with Jersey REST endpoints. I am integrating with prometheus / micrometer for generating metrics. I have exposed "/metrics" endpoint as in here
#Path("/metrics")
public class Metrics {
private static final PrometheusMeterRegistry prometheusRegistry = new PrometheusMeterRegistry(PrometheusConfig.DEFAULT);
static {
new JvmGcMetrics().bindTo(prometheusRegistry);
new JvmMemoryMetrics().bindTo(prometheusRegistry);
new JvmCompilationMetrics().bindTo(prometheusRegistry);
new JvmThreadMetrics().bindTo(prometheusRegistry);
}
#GET
public String getMetrics() {
return prometheusRegistry.scrape();
}
}
I am stuck on how to generate http request metrics. I could not find any code that would relevant to get these metrics. Can someone help me on this ?
Alternatively to what checketts proposed, you could make use of the Jersey server instrumentation of Micrometer which is present even prior to the 1.0.0 release in the form of micrometer-jersey2 library. You can find the source here.
Your entrypoint to this is the MetricsApplicationEventListener which can be registered with Jerseys ResourceConfig. For an example, you can have a look at the test class on how this could be done.
You can also have a look at how this is integrated/autoconfigured in Spring Boot here.
One last note: Spring Boots metric name is http.server.requests (to distinguish them from HTTP client request metrics) and if you one day will move to Spring Boot or your platform is already running Spring Boot applications, your non Spring Boot HTTP requests metrics will nicely match without further ado.
You'll need to include a Filter to record each request as it comes through. See https://github.com/spring-projects/spring-boot/blob/master/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/web/servlet/WebMvcMetricsFilter.java for how Spring does it.
I would recommend against using a static registry if possible and using dependency injection instead.
Here is a tiny example of what you might do within a filter's doFilter method
long start = System.nanoTime()
try {
filterChain.doFilter(request, response);
long durationNanos = System.nanoTime() - start;
prometheusRegistry.timer("http.server.requests", "status", response.getStatus().toString()).record(durationNanos, TimeUnit.NANOSECONDS)
} catch (Exception ex) {
//Equivalent exception timer recording
throw ex;
}

How to set ViewResolver in Spring WebFlux

I've my Spring Boot Web Flux application for quite some time. At this point I wanted to add some exception handlers to my router function:
#Bean
RouterFunction<?> router(final GeneratorHandler generatorHandler) {
return resources("/**", new ClassPathResource("/static/"))
.andOther(route(GET("/generate"), generatorHandler::renderData)
.andRoute(GET("/index"), generatorHandler::renderIndex));
}
So I added another bean like this:
#Bean
HttpHandler httpHandler(final GeneratorHandler generatorHandler) throws Exception {
return WebHttpHandlerBuilder.webHandler(toHttpHandler(router(generatorHandler)))
.prependExceptionHandler((serverWebExchange, exception) -> {
//process here
return null;
})
.build();
}
After this I am having troubles with my view resolver. It can't find any of my views. After investigating I've realized that debugger does not stop in ThymeleafReactiveViewResolver class.
Is that possible that this change changed default view resolver too? How Can I bring it back?
Providing your httpHandler disables quite a lot of support from Spring Boot.
You can declare your own WebExceptionHandler, as a component (even an ordered one) and Spring WebFlux will pick it up for you. Without a more concrete example (or at least the stacktrace/error you're seeing), it's hard to understand what's going on.
Spring Boot now supports error handling in WebFlux applications (see #8625), in case it does what you're trying to achieve.

A real-world controller example with Spring 5: Web Reactive

I want to be involved in a reactive programming world with Spring. As I realised, it gives me a choice between two different paradigms: the annotation-based (with well-known to us #Controller, #RequestMapping) and the reactive one (which is intended to resolve an "Annotation Hell").
My problem is a lack of understanding how a typical reactive controller will look like. There are three conceptual interfaces, which I can use in my controller class:
HandlerFunction<T> (1) - I define a method for each specific ServerRequest
which returns a concrete HandlerFunction<T> instance, then register these methods with a router. Right?
RouterFunction (2) and FilterFunction (3) - Is there a specific place where all RequestPredicates with corresponding HandlerFunctions should be placed? Or can I do it separately in each controller (as I used to do with the annotation approach)? If so, how then to notify a global handler (router, if any?) to apply this router part from this controller?
It's how I see a reactive controller "template" by now:
public class Controller {
// handlers
private HandlerFunction<ServerResponse> handleA() {
return request -> ok().body(fromObject("a"));
}
// router
public RouterFunction<?> getRouter() {
return route(GET("/a"), handleA()).and(
route(GET("/b"), handleB()));
}
// filter
public RouterFunction<?> getFilter() {
return route(GET("/c"), handleC()).filter((request, next) -> next.handle(request));
}
}
And, finally, how to say that it is a controller, without marking it with the annotation?
I've read the Spring reference and all posts related to this issue on the official blog. There is a plenty of samples, but all of them are pulled out of context (IMHO) and I can't assemble them into a full picture.
I would appreciate if you could provide a real-world example and good practices of how to organise interactions between these functions.
This is not a real world example, but so far Is how I view some kind of organization on this:
https://github.com/LearningByExample/reactive-ms-example
As far as I concerned:
RouterFunction is the closest analogue to #Controller (#RequestMapping precisely) in terms of new Spring approach:
Incoming requests are routed to handler functions with a
RouterFunction (i.e. Function>). A router function evaluates to a
handler function if it matches; otherwise it returns an empty result.
The RouterFunction has a similar purpose as a #RequestMapping
annotation. However, there is an important distinction: with the
annotation your route is limited to what can be expressed through the
annotation values, and the processing of those is not trivial to
override; with router functions the processing code is right in front
of you: you can override or replace it quite easily.
Then instead of Spring Boot SpringApplication.run in main method your run server manually by :
// route is your route function
HttpHandler httpHandler = RouterFunctions.toHttpHandler(route);
HttpServlet servlet = new ServletHttpHandlerAdapter(httpHandler);
Tomcat server = new Tomcat();
Context rootContext = server.addContext("",
System.getProperty("java.io.tmpdir"));
Tomcat.addServlet(rootContext, "servlet", servlet);
rootContext.addServletMapping("/", "servlet");
tomcatServer.start();
There are both reactive and non-reactive approach. It's illustrated on Spring github

Spring Boot REST API - request timeout?

I have a Spring Boot REST service that sometimes call third party services as a part of a request. I would like to set a timeout on all my resources (let's say 5 seconds), so that if any request handling (the whole chain, from incoming to response) takes longer than 5 seconds my controllers responds with HTTP 503 instead of the actual response. It would be awesome if this was just a Spring property, for example setting
spring.mvc.async.request-timeout=5000
but I haven't had any luck with that. I've also tried extending WebMvcConfigurationSupport and overriding configureAsyncSupport:
#Override
public void configureAsyncSupport(final AsyncSupportConfigurer configurer) {
configurer.setDefaultTimeout(5000);
configurer.registerCallableInterceptors(timeoutInterceptor());
}
#Bean
public TimeoutCallableProcessingInterceptor timeoutInterceptor() {
return new TimeoutCallableProcessingInterceptor();
}
without any luck.
I suspect I have to manually time all my third party calls, and if they take too long, throw a timeout exception. Is that right? Or is there any easier, holistic solution that covers all my request endpoints?
You need to return a Callable<> if you want spring.mvc.async.request-timeout=5000 to work.
#RequestMapping(method = RequestMethod.GET)
public Callable<String> getFoobar() throws InterruptedException {
return new Callable<String>() {
#Override
public String call() throws Exception {
Thread.sleep(8000); //this will cause a timeout
return "foobar";
}
};
}
The #Transactional annotation takes a timeout parameter where you can specify timeout in seconds for a specific method in the #RestController
#RequestMapping(value = "/method",
method = RequestMethod.POST,
produces = MediaType.APPLICATION_JSON_VALUE)
#Timed
#Transactional(timeout = 120)
A fresh answer for Spring Boot 2.2 is required as server.connection-timeout=5000 is deprecated. Each server behaves differently, so server specific properties are recommended instead.
SpringBoot embeds Tomcat by default, if you haven't reconfigured it with Jetty or something else. Use server specific application properties like server.tomcat.connection-timeout or server.jetty.idle-timeout.
As commented by Wilkinson:
Setting the connection timeout will only result in a timeout when the
client connects but is then too slow to send its request. If you want
the client to wait for a maximum of 30 seconds for a response you will
have to configure that on the client-side. If you want the server-side
to only spend a maximum of 30 seconds handling the request there is no
way to guarantee that as you cannot force the thread that is handling
the request to stop.
You can also try setting spring.mvc.async.request-timeout
I would suggest you have a look at the Spring Cloud Netflix Hystrix starter to handle potentially unreliable/slow remote calls. It implements the Circuit Breaker pattern, that is intended for precisely this sorta thing.
See offcial docs for more information.
You can try server.connection-timeout=5000 in your application.properties. From the official documentation:
server.connection-timeout= # Time in milliseconds that connectors will
wait for another HTTP request before closing the connection. When not
set, the connector's container-specific default will be used. Use a
value of -1 to indicate no (i.e. infinite) timeout.
On the other hand, you may want to handle timeouts on the client side using Circuit Breaker pattern as I have already described in my answer here: https://stackoverflow.com/a/44484579/2328781
if you are using RestTemplate than you should use following code to implement timeouts
#Bean
public RestTemplate restTemplate() {
return new RestTemplate(clientHttpRequestFactory());
}
private ClientHttpRequestFactory clientHttpRequestFactory() {
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
factory.setReadTimeout(2000);
factory.setConnectTimeout(2000);
return factory;
}}
The xml configuration
<bean class="org.springframework.web.client.RestTemplate">
<constructor-arg>
<bean class="org.springframework.http.client.HttpComponentsClientHttpRequestFactory"
p:readTimeout="2000"
p:connectTimeout="2000" />
</constructor-arg>
In Spring properties files, you can't just specify a number for this property. You also need to specify a unit. So you can say spring.mvc.async.request-timeout=5000ms or spring.mvc.async.request-timeout=5s, both of which will give you a 5-second timeout.
I feel like none of the answers really solve the issue. I think you need to tell the embedded server of Spring Boot what should be the maximum time to process a request. How exactly we do that is dependent on the type of the embedded server used.
In case of Undertow, one can do this:
#Component
class WebServerCustomizer : WebServerFactoryCustomizer<UndertowServletWebServerFactory> {
override fun customize(factory: UndertowServletWebServerFactory) {
factory.addBuilderCustomizers(UndertowBuilderCustomizer {
it.setSocketOption(Options.READ_TIMEOUT, 5000)
it.setSocketOption(Options.WRITE_TIMEOUT, 25000)
})
}
}
Spring Boot official doc: https://docs.spring.io/spring-boot/docs/2.2.0.RELEASE/reference/html/howto.html#howto-configure-webserver
You can configure the Async thread executor for your Springboot REST services. The setKeepAliveSeconds() should consider the execution time for the requests chain. Set the ThreadPoolExecutor's keep-alive seconds. Default is 60. This setting can be modified at runtime, for example through JMX.
#Bean(name="asyncExec")
public Executor asyncExecutor()
{
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(3);
executor.setMaxPoolSize(3);
executor.setQueueCapacity(10);
executor.setThreadNamePrefix("AsynchThread-");
executor.setAllowCoreThreadTimeOut(true);
executor.setKeepAliveSeconds(10);
executor.initialize();
return executor;
}
Then you can define your REST endpoint as follows
#Async("asyncExec")
#PostMapping("/delayedService")
public CompletableFuture<String> doDelay()
{
String response = service.callDelayedService();
return CompletableFuture.completedFuture(response);
}

Categories

Resources