I have several applications monitored under Spring Boot Admin. Spring Boot Admin is great at telling me if an application is up or down and other various metrics.
I would also like to know that certain URLs exposed by these applications are returning an HTTP status of 200. Specifically, I would like to send a GET request to these URLs once a day. If it receives a non 200 status from any of these, it sends an email stating which URLs are reporting non 200.
Is something that Spring Boot Admin is able to do? I know about custom HealthIndicators but not sure if it can be scheduled or if it's appropriate for this.
Just wanted to see if there is something Spring Boot Admin offers to support doing this before I build my own app to make the GET calls and send the email.
Update
The URLs are exposed as Eureka services and I'm calling services from other services via Spring Cloud OpenFeign.
Update 2
I went ahead and built my own custom application to handle this. Details follow but still interested if Spring offers something out-of-the-box to do this.
application.yml
app:
serviceUrls:
- "http://car-service/cars?category=sedan"
- "http://truck-service/trucks"
cron: "0 0 10 * * *"
Urls are read into:
#Component
#ConfigurationProperties(prefix = "app")
#Getter
#Setter
public class ServiceUrls {
private String[] serviceUrls;
}
Via cron, scheduled to run once a day:
#Component
#RequiredArgsConstructor
#Slf4j
public class ServiceCheckRunner {
private final ServiceHealth serviceHealth;
#Scheduled(cron = "${cron}")
public void runCheck() {
serviceHealth.check();
}
}
This is the code that checks whether URLs return no errors:
#Service
#RequiredArgsConstructor
#Slf4j
public class ServiceHealth {
private final ServiceUrls serviceUrls;
private final RestTemplate rest;
public void check() {
List<String> failedServiceUrls = new ArrayList<>();
for (String serviceUrl : serviceUrls.getServiceUrls()) {
try {
ResponseEntity<String> response = rest.getForEntity(serviceUrl, String.class);
if (!response.getStatusCode().is2xxSuccessful()) {
failedServiceUrls.add(serviceUrl);
}
} catch (Exception e){
failedServiceUrls.add(serviceUrl);
}
}
// code to send an email with failedServiceUrls.
}
}
You can use Spring Boot Admin in order to send email notifications whenever a registered client changes his status from UP to OFFLINE or otherwise.
pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
<version>2.4.0</version>
</dependency>
application.properties
spring.mail.host=smtp.example.com
spring.mail.username=smtp_user
spring.mail.password=smtp_password
spring.boot.admin.notify.mail.to=admin#example.com
But, if you really need to check client status once per day, you need to implement a custom solution.
Related
I have 2 microservices, one of them producing APPLICATION_NDJSON data fetched from ReactiveMongoRepository
#RestController
#RequestMapping("/documents")
#RequiredArgsConstructor
public class DocumentController {
private final ReactiveMongoRepository<DocumentWrapper, ObjectId> repository;
#GetMapping(produces = MediaType.APPLICATION_NDJSON_VALUE)
public Flux<DocumentWrapper> getAll() {
return repository.findAll();
}
}
The another one consumes it using WebClient
public Flux<DocumentWrapper> getDocuments() {
return webClient.method(HttpMethod.GET)
.uri(properties.getUrl() + "/documents")
.contentType(MediaType.APPLICATION_NDJSON)
.retrieve()
.bodyToFlux(DocumentWrapper.class);
}
Recently, I discovered that sometimes the producing microservice stops streaming data after a while with no reason - no error or no cancel command is generated. Why does it happen and how could it be fixed?
I implemented a project demonstrating this behavior, you could find it on GitHub
P.S.
I guess the problem is in integration between reactive mongodb driver and spring rest controller (or netty). I replaced database access
repository.findAll()
with mock data like
Flux.generate(...)
and the problem was gone.
I also replaced data streaming from database to file not to controller and it worked fine too.
I define a filter in my spring gateway(2.2.8.RELEASE) project like this:
#Component
public class LogFilter2 extends AbstractGatewayFilterFactory {
#Override
public GatewayFilter apply(Object config) {
return (exchange,chain) -> {
System.out.println("LogFilter2 flitered!!!");
return chain.filter(exchange);
};
}
}
then config the filter in application.properties like this:
# dolphin music
spring.cloud.gateway.discovery.locator.enabled=true
spring.cloud.gateway.discovery.locator.lower-case-service-id=true
spring.cloud.gateway.routes[0].id=dolphin-music-service
# forward by ip:port way
spring.cloud.gateway.routes[0].uri=http://10.107.64.246:11014
# forward by service name way
# spring.cloud.gateway.routes[0].uri=lb://
spring.cloud.gateway.routes[0].predicates[0]=Path=/music/**
spring.cloud.gateway.routes[0].filters[0]=LogFilter2
but when I run the project and send a request to the url /music/xxxxxx, the request did not enter the filter.No log LogFilter2 flitered!!! output.what should I do to make the filter works as expect? I also tried many other way,this is the minimal demo:https://github.com/jiangxiaoqiang/java-learn. In this demo, I define different kind of gateway filter, no one work except the global gateway filter. I am struggle with this problem for days.
In my Spring boot controller, I am having a method that inserts some records to the backend, at the end of this operation, I notify the user via Javax email based on the response received from from previous operation.
Currently I get response from API after email method completes.
Is there any way I can return response to the client once my first operation is completed while email notification happens in background
I tried already implement Async annotation in sendemail method of mail service. But I cannot find any difference in response time and I still get the response only after the email is sent.
My pseudo code
Controller:
#Autowired
private EmailService emailService;
#PostMapping(value = "create", produces = "text/plain")
private insertRecord()
{
response = <Insert into DB>;
sendEmail(response);
}
private sendEmail(response)
{
//check if email should be sent and if yes
emailservice.send(response);
}
Email service:
#Service
public class EmailService {
#Async
public static void sendEmail(MailEvent mailEvent) throws IOException {//send
email}
}
Starter
#SpringBootApplication
#EnableAsync
public class Starter {...}
i didn't notice my async method was designed as static, removing static works for me.
I think Spring Async is the way to go to solve your problem. Did you you also enable the async functionality with #EnableAsync and create an Executor bean? See this guide for the full tutorial: https://spring.io/guides/gs/async-method/
I am using Jhipster(Angular + Springboot) Application for my existing project.
I managed to create a controller(app.resource) manually apart from the ones already generated by jhiptser(using .jh file) for achieving a file download functionality.
So, when we start the server we usually initiate two servers i.e gradlew and npm start. The second runs on port 9000 which eventually supports hot reload functionality.(front-end development)
So the problem is, I am able to access those endpoints from the server running on standard 8000 port. However, from the port which is a proxy(9000), the method is returning 404.
I tried to clean build the application several times.
NOTE: The #RequestMapping value on the new controller is different then those present already.
Does this have to do something with spring security?
Thanks in advance.
Here is the previous controller:
#RestController
#RequestMapping("/api")
public class FGAppDiagramResource {
#GetMapping(value = "/fg-app-diagram-downloadFile")
public void getImage(String fileName,String folderName, HttpServletResponse
response){
// Some Code
}
}
Here is my New controller:
#RestController
#RequestMapping("/fileDownload")
public class DownloadFileController {
private final Logger log =
LoggerFactory.getLogger(DownloadFileController.class);
public DownloadFileController() {
super();
}
#Autowired
private ApplicationProperties applicationProperties;
#GetMapping(value = "/fg-app-diagram-downloadFile/{fileName}/{folderName}")
public void getImage(#PathVariable String fileName,#PathVariable String folderName, HttpServletResponse response) {
// Some Code
}
}
Your new controller does not use /api so you must add your endpoint URL /fileDownload to proxy configuration of webpack dev server in webpack/webpack.dev.js
proxy: [{
context: [
/* jhipster-needle-add-entity-to-webpack - JHipster will add entity api paths here */
'/api',
'/fileDownload',
You may want to use /api/fileDownload to avoid changing proxy configuration and also because /api is useful for many other aspects like security and also using HTML5 URL routing strategy in Angular to get rid of # in client routes (see https://github.com/jhipster/generator-jhipster/pull/9098).
/api and /management are namespaces to avoid route conflicts, so it is usually wise to use them for your new endpoints.
I have a Spring boot application that uses the actuator metrics and I have the following issue:
I have an endpoint like this :
GET /users/{userId}
So every time I call this endpoint I use a different Id to get the specific user as you can think we can have hundreds of thousands. It is working correct, but i noticed that when calling my metrics endpoint :
GET /metrics
I get something like this:
counter.status.200.metrics: 1,
counter.status.200.api.users.4: 2,
counter.status.200.api.users.2: 3,
counter.status.200.api.users.3: 2,
So it makes me think that i will get a counter for every single call with different path params, so my question is how can i have a counter just for the endpoint /users/{anyId} and not for every single combination excluding the parameters?.
--- EDIT ---
I'm using Spring boot + Jersey (I'm not using Spring MVC), the following is my controller code:
#Component
#Path("/users")
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public class UserResource {
#GET
public Response getUsers() {
return Response.ok("It works !").build();
}
#GET
#Path("/{userId}")
public Response getUserById(#PathParam("userId") String id) {
return Response.ok("It works !").build();
}
}
And the following is the Jersey configuration:
#Component
#ApplicationPath("/api")
public class JerseyConfig extends ResourceConfig {
public JerseyConfig() {
register(UserResource.class);
register(PingResource.class);
}
}
It's a known issue with Spring Boot 1.x when using Jersey. There's a workaround described in the issue, alternatively you can disable the metrics filter by adding the following to application.properties:
endpoints.metrics.filter.enabled=false