How to get webflux webClient metrics from custom Webclient Builder - spring-boot

I have created a custom Webclient Builder instead of injecting the default builder.
#Configuration
public class WebClientConfig() {
#Bean(name = "myWebClientBuilder")
public Webclient.Builder customBuilder() {
return WebClient.builder();
}
}
I have multiple services where I use this bean myWebClientBuulder and do further customization with chain of ExchangeFilterFunction.
This might not be the recommended way of using the WebClient but I would like to get some insight if there is a way to get the downstream call metrics from the Webclient based on this configuration.
Actuator Endpoint: actuator/metrics/http.client.requests

Spring Boot auto-configured WebClient.Builder is way powerful than customized version.
I tried to configure the custom builder in WebClientConfig() but it started to structure just like a copy version of WebClientAutoConfiguration. I ended up going with the spring boot autoconfigured WebClient.Builder bean.
If it helps, you can study how WebClientAutoConfiguration tries to configure webClient customizers. For metrics, it would be MetricsWebClientCustomizer.

Related

Spring Boot metrics http.client.requests do not work for WebClient reactive applications

I have a spring boot 2.2.2 microservices, which integrations with other services using WebClient (rective). According to Spring documentation, the actuator should return "http.client.requests" metrics by default as Timer is enabled by default. But it does not work for me. I am able to get http.server.requests" metrics.
My WebClient is a bean configured and build with WebClient.builder(), as documented here: https://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-features.html#production-ready-metrics-http-clients
Use Spring Boot's preconfigured WebClient.Builder instead of WebClient.builder() to have an instance of WebClient.
You can find more detail here.
As in the following you can have a bean of WebClient.
#Configuration
public class ClientConfiguration {
#Bean
public WebClient webClient(WebClient.Builder webClientBuilder) {
return webClientBuilder
.baseUrl("https://example.org")
.build();
}
}
WebClient.Builder is an Auto-configuration, means the injected point above will receive a newly cloned instance of the builder.
Here is the source for WebClientAutoConfiguration
I've noticed my application has to make a WebClient call before the metrics/http.client.requests endpoint exists. Prior to my application making a WebClient call that endpoint isn't discoverable and returns NOT FOUND.
Hope that helps others in a similar situation!

Using Gson instead of Jackson in Spring Webflux

We have many Spring MVC projects already, which all use gson instead of jackson for response body encode. Our bean classes are all written based on gson annotation. Now I am setting up a Spring Webflux restful server. It would save a lot of work if we can use the old bean classes from our Spring MVC projects.
I have tried spring.http.converters.preferred-json-mapper=gson property to no avail.
I have tried HttpMessageConverter bean, which is included in webflux packages, but that does not work as in the Spring MVC projects.
I googled a lot and the only thing helpful is to implement org.springframework.http.codec.HttpMessageEncoder class and set it to WebFluxConfigurer.configureHttpMessageCodecs() method:
#Configuration
public class WebConfiguration implements WebFluxConfigurer {
#Override
public void configureHttpMessageCodecs(ServerCodecConfigurer configurer) {
configurer.customCodecs().decoder(new GsonHttpMessageDecoder());
configurer.customCodecs().encoder(new GsonHttpMessageEncoder());
}
private static class GsonHttpMessageEncoder implements HttpMessageEncoder {
...
}
private static class GsonHttpMessageDecoder implements HttpMessageDecoder {
...
}
}
I haven't try this out yet, since it is a little complex. Is there some easy way to replace jackson with gson in Spring Webflux?
Any help is appreciated.
Spring Framework doesn't support GSON as a WebFlux Encoder / Decoder for now. Feel free to follow up on the dedicated issue.
Note that as far as I know, GSON doesn't support non-blocking parsing so even if the support is implemented in Framework, it won't be complete and should not cover streaming input use cases.

JerseyConfig and #Profile to hide a REST endpoint

I'm trying to hide a REST endpoint based on runtime configuration in Spring and Jersey. The most straightforward way is to throw the NotFoundException from the controller itself but maybe there's more kosher. The controller is registered in the constructor of the config class which extends org.glassfish.jersey.server.ResourceConfig.
I thought of using the #Profile annotation on the controller but I can still access the endpoint. When I hit that endpoint, I get the following error:
o.g.j.s.s.SpringComponentProvider - None or multiple beans found in Spring context
but then Jersey manages to access that controller, which I confirmed by attaching a debugger to the Spring process. So Jersey does not honor the #Profile setting.
On a separate note, I also have Swagger plugged into Jersey and when accessing the definition endpoint (.../swagger.json) I can see the endpoint provided by the #Profile-disabled controller.
Is there anything better I can do here is is throwing the NotFoundException the best option?
Note: Sorry, I thought I saw that you were using Spring Boot. The following answer is only relevant for Spring Boot.
#Profile is only good for Spring bean registration, but you are still registering the service with Jersey as a resource. What you can do is use a ResourceConfigCustomizer and add the #Profile to the customizer. This way it will only register the resource with Jersey ResourceConfig if the correct profile is active.
#Component
#Profile("..")
public class MyResourceConfigCustomizer implements ResourceConfigCustomizer {
#Override
public void customize(ResourceConfig config) {
config.register(MyResource.class);
}
}

how to use ApplicationEventPublisher in spring integration with annotation?

I am new to spring integration and I have to do some event based processing ? can anyone tell me how to use ApplicationEventPublisher. sample will be very helpful.
For publishing Spring Application events Spring Integration provides ApplicationEventPublishingMessageHandler component. This is one-way, just send producer and should be configured together with the #ServiceActivator annotations:
#ServiceActivator(inputChannel = "sendEventChannel")
#Bean
public MessageHandler eventProducer() {
return new ApplicationEventPublishingMessageHandler();
}
Also see http://docs.spring.io/spring-integration/reference/html/applicationevent.html.

Spring Boot Jersey and Monitoring URL's

We have a simple Spring Boot application with Jersey.
Spring Boot provides default monitoring end points
http://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#production-ready-monitoring
Example:
#Component
public class JerseyConfig extends ResourceConfig {
public JerseyConfig() {
// registering resources from rest package
packages("com.xyx.abc.rest");
}
}
The REST end points that are provided by Spring Boot are not available in the context of a Spring Boot Jersey Application.
The Spring Boot dependency includes Jersey, starter-actuator, starter-tomcat.
Our REST resources show up fine, but the ones provided by Spring Boot for monitoring dont show up.
E.g http://abc.xyx.com:8080/health returns a 404
If you are using it as a Filter you need to tell Jersey not to handle those requests. E.g. by putting the Jersey resources under a separate path, like "/api/*" or something:
#Bean
public FilterRegistrationBean jersey() {
FilterRegistrationBean bean = new FilterRegistrationBean();
...
bean.setUrlPatterns(Lists.newArrayList("/api/*"));
return bean;
}
(from here).
Or by declaring that your admin endpoints are "static" (via another init parameter "com.sun.jersey.config.property.WebPageContentRegex"):
#Bean
public FilterRegistrationBean jersey() {
FilterRegistrationBean bean = new FilterRegistrationBean();
...
bean.addInitParameter("com.sun.jersey.config.property.WebPageContentRegex",
"/admin/.*");
return bean;
}
where we have set management.contextPath=/admin in the Spring Boot external configuration (otherwise you'd have to enumerate all the endpoints in the regex).
You can also tell Jersey to ignore unresolved requests (instead of sending a 404). That would also achieve your goal, but might affect your client apps (if they rely on a 404 for their behaviour).

Resources