Spring Actuator: Configure multiple Prometheus endpoint with different data - java

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

Related

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 make endpoints initialization asynchronous in Spring Boot?

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.

How to invalidate browser cache in a web application built with angular7 and springboot

I'm developing a web application using springboot for the backend serving rest API and Angular7 for the frontend.
my application takes so much time to load because it has to perform a lot of processing on the server, so I decided to improve the performance by storing the cached data in order to load the page first and eventually update the data when processing ends.
This works but I'm having a problem:
When I update data in Angular these are normally saved in my database but if I update the page I don't see the changes because the browser continues to access the old cached data rather than getting the new ones modified.
Is there a way to invalidate certain data in the browser cache when they are modified?
Springboot:
My rest controller endpoint are similar to that:
#GetMapping(value = "/users")
public Iterable<User> getAllUsers(HttpServletResponse response) {
response.setHeader("Cache-Control", "max-age=3600");
return this.userService.findAll());
}
My service:
#Cacheable("users")
public Iterable<User> findAll() {
return this.userRepository.findAll();
}
My angular service:
getUsers(): Observable<User[]> {
return this.http.get<User[]>(`<ip>' + '/users');
}
I can see you're caching on the server side, you can evict cache by calling method annotated with #CacheEvict with the key you want to evict:
#CacheEvict(value = "users", allEntries = true)
Or you can do this programmatically by using CacheManager:
#Autowired
CacheManager cacheManager;
public void evictSingleCacheValue(String cacheName, String cacheKey) {
cacheManager.getCache(cacheName).evict(cacheKey);
}
You can check if there is any changes in your server side Time-based or Content-based controls by lightweight requests.
Time-based => your can use Last=Modified in the header
Content-based => you can use Etag
please check these two links;
https://devcenter.heroku.com/articles/increasing-application-performance-with-http-cache-headers
https://www.logicbig.com/quick-info/web/last-modified-and-if-modified-since.html

How to create a java client for a Spring service?

I have a service definition using Spring annotations. Example (source):
#RequestMapping(value = "/ex/foos/{id}", method = GET)
#ResponseBody
public String getFoosBySimplePathWithPathVariable(
#PathVariable("id") long id) {
return "Get a specific Foo with id=" + id;
}
The question is whether spring (or another library) can auto-create a remote implementation (client) of the same API without the need to manually type paths, method type, param names, etc. (like needed when using RestTemplate)?
Example of an such a client usage:
FooClient fooClient = new FooClient("http://localhost:8080");
String foo = fooClient.getFoosBySimplePathWithPathVariable(3l);
How can I get to such a client "generated" implementation"?
You are probably looking for Feign Client. It does everything you need: calling one service via HTTP is similar to calling method of Java interface. But to make it work you need Spring Cloud, standard Spring framework doesn't have this feature yet.
You can generate it using Swagger Editor. You shoud just define the path of the resources and then it'll generate for you the client for almost any language of your choice

How seperate Front-End & Back-End machine in spring-boot?

I want to separate Back-End and Front-End(HTML Pages) machines.The back-end will be developed by Spring-Boot. How can return View in controllers to Front-End machine instead of "resources/template" in Back-End(Spring-Boot--->Apache Tomacat) machine?
For example :
#Controller
public class GreetingController {
#RequestMapping("/greeting")
public String greeting(#RequestParam(value="name", required=false, defaultValue="World") String name, Model model) {
model.addAttribute("name", name);
return "greeting";
}
}
I want to put "greeting" view in another server (Front-End).
You didn't disclose which templating technology are you using (e.g. JSP, Thymeleaf, ...), but either way Spring needs to inject your variables from model into HTML templates.
AFAIK, there is no way to host views in one JVM and controller filling it on other JVM. You could extract your views into separate JAR, but it would need to be hosted on same Servlet container at the end of the day.
If you want true separation of client and server, investigate templating on client (Single Page Applications) and using just AJAX for fetch the data from REST back-end.
You can start two servers, one for backend and the other for frontend. The two would be communicating via REST call. The Backend server will give the data to frontend server, which will collect it and send it to html templates in the frontend server. The template engine integration would save you time in getting things done. Springboot has good integration with Thymeleaf so I would recommend you to use the same.
It's actually quite simple after you have the prototype ready. I have made the prototype for frontend and backend separated springboot applications. The template engine used here is thymeleaf, database is mysql and language is java. You can remove the unneeded part and start with your work!
You may need to implement a/the WebMvcConfigurerAdapter interface.
This is a code sample:
#Configuration
public class StaticResourceConfiguration extends WebMvcConfigurerAdapter {
#Value("${spring.thymeleaf.prefix}")
private String thymeleafTemplatePath;
#Value("${node_modules.path}")
private String nodeModulesPath;
public void addResourceHandlers(ResourceHandlerRegistry registry){
if (thymeleafTemplatePath != null && !thymeleafTemplatePath.isEmpty()){
if (!registry.hasMappingForPattern("/**")) {
registry.addResourceHandler("/**")
.addResourceLocations(thymeleafTemplatePath);
}
}
if (nodeModulesPath != null && !nodeModulesPath.isEmpty()){
if (!registry.hasMappingForPattern("/node_modules/**")) {
registry.addResourceHandler("/node_modules/**")
.addResourceLocations(nodeModulesPath);
}
}
}
}
The following code is for a configuration variable in a property file.
This example has a Windows file path pattern. You may need to change the pattern for your environment.
spring.thymeleaf.prefix=file:///C:/Users/young.k.jun/workspaces/separated-front-end/front-end/src/
node_modules.path=file:///C:/Users/young.k.jun/workspaces/separated-front-end/front-end/node_modules/
I made a sample project to separate front-end and back-end workspace so as not to conflict with their work directories.
Please refer to this link. You can find a GitHub link on that page.
It's possible to do that with REST-Web-Service but the goal from Thymeleaf isn't to work alone as frontend App. If you absolutly need to have a separated frontend App you should user any moderne js framework such as Angular/React/Vue and use spring boot for rest api.

Categories

Resources