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;
}
Related
Currently, we are experimenting with setting Prometheus up for monitoring for our services, internal as external. The problem is that we cannot configure Prometheus for some of our external services, but we would like these to be still included as a separate job in Prometheus.
I want to have 2 different Prometheus endpoints (e.g. /actuator/prometheus/api and /actuator/prometheus/thingworx) that have return different data.
/actuator/prometheus/api would have the actual data of the API (similar like if you just install the package).
/actuator/prometheus/thingworx would only return some custom metrics that we get at certain intervals from our external service.
This should, ideally, be done on a single Spring server. Can this be done with Spring Actuator and Micrometer or is this impossible?
After searching, I decided to do it another way. As you can't easily modify the prometheus endpoint itself to include other paths (Tried with WebEndpointExtension, but didn't have any success), I created my own custom endpoint that fetches data from a service that contains the main registry that is autowired by Spring Boot and another service that contains a custom registry that is updated in intervals.
#RestController
#RestControllerEndpoint(id = "multiPrometheus")
public class PrometheusController {
private final APIPrometheusService apiService;
private final ThingworxPrometheusService thingworxService;
public PrometheusController( APIPrometheusService apiService, ThingworxPrometheusService thingworxService) {
this.apiService = apiService;
this.thingworxService = thingworxService;
}
#GetMapping( value = "/api", produces = TEXT_PLAIN_VALUE)
public String getPrometheusApiStream(){
return apiService.scrape();
}
#GetMapping(value = "/thingworx", produces = TEXT_PLAIN_VALUE)
public String getPrometheusThingworxStream(){
if(thingworxService.isConnected()){
return thingworxService.scrape();
}
throw new ResponseStatusException(SERVICE_UNAVAILABLE);
}
}
This way I have full control over the path mapping of my endpoints that live under /actuator
I am trying to build out an Angular & Spring Boot app with Webflux, and instead of starting by getting bogged down by a full CRUD tutorial with entities and databases I thought I'd start by just seeing if I could get the client to display a string returned by the server, as spring boot's documentation teaches here. To that end, I have the following code:
//Angular client
private currencyGreetingUrl: string;
constructor(private http: HttpClient) {
this.currencyGreetingUrl = 'http://localhost:8080/currency';
}
public getCurrencyGreeting(): Observable<string> {
return this.http.get<string>(this.currencyGreetingUrl);
}
//Spring Boot Web Client
#CrossOrigin(origins = "http://localhost:4200")
public class CurrencyWebClient {
private WebClient client = WebClient.create("http://localhost:8080");
private Mono<ClientResponse> result = client.get()
.uri("/currency")
.accept(MediaType.TEXT_PLAIN)
.exchange();
public String getResult() {
return ">> result = " + result.flatMap(res -> res.bodyToMono(String.class)).block();
}
}
//Spring boot handler
#Component
public class CurrencyHandler {
public Mono<ServerResponse> currency(ServerRequest request) {
return ServerResponse.ok().contentType(MediaType.TEXT_PLAIN)
.body(BodyInserters.fromValue("Hello, Currency On The Go!"));
}
}
//Spring Boot router
#Configuration
public class CurrencyRouter {
#Bean
public RouterFunction<ServerResponse> route(CurrencyHandler currencyHandler) {
return RouterFunctions
.route(RequestPredicates.GET("/currency").and(RequestPredicates.accept(MediaType.TEXT_PLAIN)), currencyHandler::currency);
}
}
However, I get this error when running the client and server Access to XMLHttpRequest at 'http://localhost:8080/currency' from origin 'http://localhost:4200' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
I am new to Spring Boot and have been looking around for advice, but I've generally found solutions that involve configurations, controller classes, and other more built out artifacts than what I currently have. How could I get this to work? I appreciate any help.
You have decided to use functional endpoints and you are not using spring security of what i can tell from your tags which means all other answers provided here are faulty.
When using functional endpoints you have to be aware that you have opted out of using the traditional annotation based classes. Which means you loose a lot of the free functionality provided by the spring framework
Functional endpoints are more "low level". So the responsibility falls on you the developer to handle CORS.
If you are a beginner on Spring and webflux i would not pick this route.
The documentation states in the CORS chapter in the official webflux documentation:
You can apply CORS support through the built-in CorsWebFilter, which
is a good fit with functional endpoints.
They even provide an implementation of a standard CORS filter that you can customize to your needs.
#Bean
CorsWebFilter corsFilter() {
CorsConfiguration config = new CorsConfiguration();
// Possibly...
// config.applyPermitDefaultValues()
config.setAllowCredentials(true);
config.addAllowedOrigin("https://domain1.com");
config.addAllowedHeader("*");
config.addAllowedMethod("*");
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);
return new CorsWebFilter(source);
}
There is 100s of webpages on the internet that explain CORS and it is the most common question asked in the spring-boot, spring-security tag. I suggest you read up on CORS (which you should of done before asking here).
I highly suggest you start reading here:
Mozilla CORS
Have you configured cors properly?
If not try following:
https://stackoverflow.com/a/67605427/9295125
Please also read the second answer as the posted answer misses one line.
For my purposes when using Webflux with a browser-based front-end where I need to test quickly, I use a CORS-enabling Chrome extension to test quickly. This is obviously not great for production.
If you are in a pure microservice / backend communication situation, you may just need to go with Spring's intended workflow. In my experience, Spring Webflux has some powerful outcomes but finicky setup. Those configuration classes that you mentioned but are avoiding, or annotations or DSL-based solutions for Kotlin are, unfortunately for now, the intended way. In my experience they do work, and once you have them set up it's a matter of making a quick github-ready template for other microservices (I was building Microservices with Spring and Kotlin at the time).
EDIT: I will also just add that those configurations and classes you're avoiding are worth your time, even a morning of your time spent on those will pave the way for a lot of productivity. Webflux is great (and I've used it both with Kotlin and Java) but there are some unavoidable gotchas and configuration differences when you jump to asynchronous / reactive webflux land. The joy in the config comes when you realize you did things right and it's your DB driver that's freaking out when you can push so many requests :) :) :)
Indeed I did need to use the CorsWebFilter. However, after reading a bit on Spring Boot and its Dependency Injection system, and looking at different tangentially related cors implementation in Spring Boot projects that didn't use functional endpoints, I found that I had to place the ``CorsWebFilter``` at the root of my application like so:
#SpringBootApplication
public class ServerApplication {
public static void main(String[] args) {
SpringApplication.run(ServerApplication.class, args);
CurrencyWebClient client = new CurrencyWebClient();
System.out.println(client.getResult());
}
#Bean
CorsWebFilter corsFilter() {
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin("http://localhost:4200");
config.addAllowedHeader("*");
config.addAllowedMethod("*");
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);
return new CorsWebFilter(source);
}
}
This of course, is not the only way to do it. But it solved this little initial problem I had. Thanks all.
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.
I am using current Spring boot version (1.4.x) and wondering if it has any default timeout for api calls. I have tested it by putting breakpoints but it was keep waiting and didn't time-out.
I was also trying to configure default timeout for all my spring-boot apps by using some annotation or yml settings.
I found couple of alternatives (one of them here) but using callable actually adding extra non-business logic code where setting something in xml bean is out of fashion in latest spring boot applications.
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.
UPDATE:
Just noticed that you use microservice architecture, so in case you need to handle timeouts when communicating between microservices, I would suggest handling it on the client side instead of the server side. If the microservice you are trying to call is overloaded and its performance degrades to the point where it drastically affects the user experience sometimes it's better to return some fallback data than just drop the request.
Imagine we have an e-commerce web-site that has microservice architecture and one of its microservices that gives recommendations to the user becomes extremely slow. In this case, the preferred solution would be to return some fallback data which could be top 10 popular products this month rather than showing 5xx error page to the customer. Moreover, in case subsequent requests fail with a timeout, we can make a decision to avoid sending requests to the 'recommendation-service' and return fallback data immediately. After some time we can try sending a request to the 'recommendation-service' again, and if it became healthy - just use it instead of the fallback data.
This is called Circuit Breaker pattern and there is already an implementation of it in a framework called Hystrix. Here is a nice article explaining it in depth: http://www.baeldung.com/spring-cloud-netflix-hystrix. Spring Cloud Feign + Spring Cloud Hystrix looks really nice especially taking into account that they work with Discovery services out-of-the-box (e.g. Spring Cloud Eureka).
I agree all above options and tried below option in my spring boot application. It works perfectly fine now. Below is the code sample as a bean. Now just need to #Autowire RestTemplate wherever(java class) I need it.
#Bean
public RestTemplate restTemplate() {
RestTemplate restTemplate = new RestTemplate();
((SimpleClientHttpRequestFactory) restTemplate.getRequestFactory()).setConnectTimeout(15000);
((SimpleClientHttpRequestFactory) restTemplate.getRequestFactory()).setReadTimeout(15000);
return restTemplate;
}
There are a couple of ways to do this:
1) Using ClientHttpRequestFactory with RestTemplate:
public RestTemplate restTemplate() {
return new RestTemplate(clientHttpRequestFactory());
}
private ClientHttpRequestFactory clientHttpRequestFactory() {
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
factory.setReadTimeout(timeinMillis);
factory.setConnectTimeout(timeinMillis);
return factory;
}
2) Second way is to use callable but I guess you have already explored that solution.
The timeout can be set using the connectionTimeout property of Tomcat.
Please refer this answer how to set it for Tomcat.
Configuring maxKeepAliveRequests in Spring Boot embedded Tomcat
You can create a configuration file using annotation #Configuration
For using RestTemplate:
` #Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return new RestTemplateBuilder()
.setConnectTimeout(Duration.ofMillis(30000))
.setReadTimeout(Duration.ofMillis(30000))
.build();
}`
For WebClient Timeouts:
#Bean
public WebClient webClient() {
return WebClient.builder()
.clientConnector(new ReactorClientHttpConnector(
HttpClient.create().responseTimeout(Duration.ofMillis(30000))
))
.build();
}
After the configuration is done, in a controller or any other file where you want to use this RestTemplate or WebClient just autowire it like:
#Autowired
private RestTemplate restTemplate;
or
#Autowired
private WebClient webClient;
I googled some information about web services, it seems like a enterprise level application. I found that RESTful design is very cool idea on this. I find that Apache CXF looks cool, it support RESTful design and Java. It is a good choice for beginner to start writing an application using Apache CXF? or any other framework is suggested?
I'd go for Jersey, the RI of JAX-RS (JSR 311), the Java API for RESTful Web Services (i.e. a standard).
I recommend to use JAX-RS because IMHO it is the most neutral framework in terms of telling you how REST should be done. I have not used CXF, only Jersey. It is a very solid implementation and comes with a good client side connector, too (client side not part of JAX-RS yet).
Being neutral with regard to 'how to do REST' is important because there is not yet an acknowledged 'best' way to approach certain aspects (e.g. design of hypermedia).
Congrats to going the REST way - you won't regret it.
Jan
The much simpler implementation for a beginner would be spring 3.0 REST support. Spring MVC 3.0 has REST support and is very much simpler compared to Apache CXF.
Restlet in another RESTful web framework for Java : http://www.restlet.org/
I get started REST with RESTEasy and get it up in 30 minutes. You can use it as stand-alone lib in your favorite servlet container without all this JBoss stuff.
You should try PlayFramework. Just take a loot at a sample route file and you will know how easy it is to use play to implement RESTFul web app:
# ====== Order service =========================
GET /orders Orders.list
GET /orders/{<[0-9]+>id} Orders.show
PUT /orders/{<[0-9]+>id} Order.saveUpdate
POST /orders Orders.saveNew
# ==============================================
And corresponding controller methods:
public class Orders extends Controller {
public static void list() {
List<Order> orders = Order.all();
render(orders);
}
public static void show(long id) {
Order order = Order.findById(id);
notFoundIfNull(order);
render(order);
}
public static void saveUpdate(long id, Order update) {
Order order = Order.findById(id);
notFoundIfNull(order);
order.update(update);
show(id);
}
public static void saveNew(Order order) {
order.save();
show(order.getId());
}
}
There are some utilities enable you to interact with other Web Services:
String url = "https://ajax.googleapis.com/ajax/services/search/web";
Map<String, Object> params = new HashMap<String, Object>();
params.put("v", "1.0");
params.put("q", searchStr);
params.put("key", Play.configuration.get("app.google.key"));
params.put("userip", myIpAddr);
HttpResponse resp = WS.url(url).params(params).get();
return resp.getString();