Replace org.springframework.cloud.openfeign.ribbon.CachingSpringLoadBalancerFactory - spring

When I upgrade implementation 'org.springframework.cloud:spring-cloud-openfeign-core:2.2.2.RELEASE' to latest version implementation 'org.springframework.cloud:spring-cloud-openfeign-core:3.1.1'
I get error for this imported class:
import org.springframework.cloud.openfeign.ribbon.CachingSpringLoadBalancerFactory
import org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient;
#Bean
public Client client(
HttpClientConnectionManager httpClientConnectionManager,
CachingSpringLoadBalancerFactory lbClientFactory,
SpringClientFactory clientFactory) {
CloseableHttpClient closeableHttpClient = HttpClients.custom()
.setConnectionManager(httpClientConnectionManager)
.build();
ApacheHttpClient delegate = new ApacheHttpClient(closeableHttpClient);
return new LoadBalancerFeignClient(delegate, lbClientFactory, clientFactory);
}
Do you know how I have to replace them in order to use the latest version?

ribbon was excluded from that spring boot version, you should now use loadbalancer instead. Check this class org.springframework.cloud.openfeign.loadbalancer.FeignLoadBalancerAutoConfiguration, from my POV, you just need to remove all your ribbon dependencies from the code and that's it.

Related

How to redirect Prometheus Metrics to the default spring boot server

I am trying to expose a custom Gauge metric from my Spring Boot Application. I am using Micrometer with the Prometheus registry to do so. I have set up the PrometheusRegistry and configs as per - Micrometer Samples - Github but it creates one more HTTP server for exposing the Prometheus metrics. I need to redirect or expose all the metrics to the Spring boot's default context path - /actuator/prometheus instead of a new context path on a new port. I have implemented the following code so far -
PrometheusRegistry.java -
package com.xyz.abc.prometheus;
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.time.Duration;
import com.sun.net.httpserver.HttpServer;
import io.micrometer.core.lang.Nullable;
import io.micrometer.prometheus.PrometheusConfig;
import io.micrometer.prometheus.PrometheusMeterRegistry;
public class PrometheusRegistry {
public static PrometheusMeterRegistry prometheus() {
PrometheusMeterRegistry prometheusRegistry = new PrometheusMeterRegistry(new PrometheusConfig() {
#Override
public Duration step() {
return Duration.ofSeconds(10);
}
#Override
#Nullable
public String get(String k) {
return null;
}
});
try {
HttpServer server = HttpServer.create(new InetSocketAddress(8081), 0);
server.createContext("/sample-data/prometheus", httpExchange -> {
String response = prometheusRegistry.scrape();
httpExchange.sendResponseHeaders(200, response.length());
OutputStream os = httpExchange.getResponseBody();
os.write(response.getBytes());
os.close();
});
new Thread(server::start).run();
} catch (IOException e) {
throw new RuntimeException(e);
}
return prometheusRegistry;
}
}
MicrometerConfig.java -
package com.xyz.abc.prometheus;
import io.micrometer.core.instrument.MeterRegistry;
public class MicrometerConfig {
public static MeterRegistry carMonitoringSystem() {
// Pick a monitoring system here to use in your samples.
return PrometheusRegistry.prometheus();
}
}
Code snippet where I am creating a custom Gauge metric. As of now, it's a simple REST API to test - (Please read the comments in between)
#SuppressWarnings({ "unchecked", "rawtypes" })
#RequestMapping(value = "/sampleApi", method= RequestMethod.GET)
#ResponseBody
//This Timed annotation is working fine and this metrics comes in /actuator/prometheus by default
#Timed(value = "car.healthcheck", description = "Time taken to return healthcheck")
public ResponseEntity healthCheck(){
MeterRegistry registry = MicrometerConfig.carMonitoringSystem();
AtomicLong n = new AtomicLong();
//Starting from here none of the Gauge metrics shows up in /actuator/prometheus path instead it goes to /sample-data/prometheus on port 8081 as configured.
registry.gauge("car.gauge.one", Tags.of("k", "v"), n);
registry.gauge("car.gauge.two", Tags.of("k", "v1"), n, n2 -> n2.get() - 1);
registry.gauge("car.help.gauge", 89);
//This thing never works! This gauge metrics never shows up in any URI configured
Gauge.builder("car.gauge.test", cpu)
.description("car.device.cpu")
.tags("customer", "demo")
.register(registry);
return new ResponseEntity("Car is working fine.", HttpStatus.OK);
}
I need all the metrics to show up inside - /actuator/prometheus instead of a new HTTP Server getting created. I know that I am explicitly creating a new HTTP Server so metrics are popping up there. Please let me know how to avoid creating a new HTTP Server and redirect all the prometheus metrics to the default path - /actuator/prometheus. Also if I use Gauge.builder to define a custom gauge metrics, it never works. Please explain how I can make that work also. Let me know where I am doing wrong.
Thank you.
Every time you call MicrometerConfig.carMonitoringSystem(); it is creating a new prometheus registry (and trying to start a new server)
You need to inject the MeterRegistry in your class that is creating the gauge and use the injected MeterRegistry that way.

Cannot connect to redis using spring and lettuce

I am struggling to find what could be wring here; need help.
I am using spring-data-redis 2.4.1
RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration()
redisStandaloneConfiguration.setHostname(hostname)
redisStandaloneConfiguration.setPort(6379)
redisStandaloneConfiguration.setPassword("password")
I then create lettuceClientConfigurationBuilder and specify clientName
I then use lettuceClientConfiguration and redisStandaloneConfiguration to create ClientConnectionFactory.
However, when we call getConnection() on the connection Factory, we get
WrongPass Invalid username-password pair
The same set of username-password works with Redis-CLI on cmd prompt.
Is there is something wrong in the way I am using in my java application?
Any pointer/hint towards solving this would be greatly appreciated.
Spring Boot configures LettuceConnectionFactory for you, you can specify the connection params on the application.properties file.
spring.redis.database=0
spring.redis.host=localhost
spring.redis.port=6379
spring.redis.password=yourPassword
spring.redis.timeout=60000
If you wanna do it programmatically, set the spring.redis.password in application.properties and try this:
#Configuration
class AppConfig {
#Bean
public LettuceConnectionFactory redisConnectionFactory() {
return new LettuceConnectionFactory(new RedisStandaloneConfiguration("server", 6379));
}
}
I had mistaken username for clientname set on LettuceClientConfigurationBuilder but username had to be specified on the redisstandaloneconfiguratuon.
This works for me; also please note acl was introduced only after lettuce 2.4.1 so any prior version will not work.
redisStandaloneConfiguration.setUsername(connectionFactoryConfigs.getUserName());

Eclipse Paho Mqtt - Spring Java configuration

I want to use MqTT in my SpringMVC project. In this link,the official example, creates all the objects with new keyword. As far as I know, this is not Spring style. The recommended way to do this creating bean, isn't?
I found some examples (spring-integration-mqtt, which based on eclipse-paho-mqtt) configured xml-based, but I want to make it Java based configuration. I congifured whole project Java-based. There is no .xml file in the project (not even web.xml).
If you suggest me an example with Java-config or good document about converting xml-config to java-config I will be appriciated.
Thanks in advance.
You can track the Pull Request on the matter, but let me share a piece of code to track more info here as well:
#Bean
public MessageProducer inbound() {
MqttPahoMessageDrivenChannelAdapter adapter =
new MqttPahoMessageDrivenChannelAdapter("tcp://localhost:1883", "testClient",
"topic1", "topic2");
adapter.setCompletionTimeout(5000);
adapter.setConverter(new DefaultPahoMessageConverter());
adapter.setQos(1);
adapter.setOutputChannel(mqttInputChannel());
return adapter;
}
#Bean
#ServiceActivator(inputChannel = "mqttOutboundChannel")
public MessageHandler amqpOutbound() {
MqttPahoMessageHandler messageHandler =
new MqttPahoMessageHandler("testClient", mqttClientFactory());
messageHandler.setAsync(true);
messageHandler.setDefaultTopic("testTopic");
return messageHandler;
}

"Node Discovery Disabled" in Elastic Search

I used below Java code on UBUNTU and I am getting "Node Discovery Disabled". Because of this I am not able move forward.
Could anyone please help me out solving this problem.
public static JestClient JestConfiguration(){
// Configuration
ClientConfig client = new ClientConfig.Builder("http://localhost:9200")
.multiThreaded(true).build();
System.out.println("\nclient configured via:- "+client);
// Construct a new Jest client according to configuration via factory
JestClientFactory factory = new JestClientFactory();
factory.setClientConfig(client);
System.out.println("\nJestClientFactory Via:-"+factory);
JestClient jestClient = factory.getObject();
System.out.println("\njestClient via:-"+jestClient);
//jestClient.shutdownClient();
return jestClient;
}
I am not sure what version you are using. I am using 0.1.2 and the factory I have only has a setHttpClientConfig method. So I used the HttpClientConfig instead, which extends ClientConfig. That point aside, the builder has two methods you'll want:
discoveryEnabled
discoveryFrequency
These set node node discovery and how frequently to poll.
HttpClientConfig httpClientConfig = new HttpClientConfig.Builder("http://localhost:9200")
.discoveryEnabled(true)
.discoveryFrequency(10l, TimeUnit.SECONDS)
.multiThreaded(true)
.build();
JestClientFactory factory = new JestClientFactory();
factory.setHttpClientConfig(httpClientConfig);

Using Jackson as Jersey client serializer

Is it possible to use Jackson as the serializer/marshaller for JSON data instead of JAXB when using Jersey Client API?
If so how to configure it?
OK, I found it out, it turns out to be quite simple after all:
ClientConfig cc = new DefaultClientConfig();
cc.getClasses().add(JacksonJsonProvider.class);
Client clientWithJacksonSerializer = Client.create(cc);
The JacksonJsonProvider comes from the jackson-jaxrs package.
You may skip the creation of external config and register the provider directly:
Client client = ClientBuilder.newClient().register(JacksonJsonProvider.class)
Solution with JacksonJaxbJsonProvider
Common way how to use Jackson with custom configuration in Jersey client was to use JacksonJaxbJsonProvider for example like this
JacksonJaxbJsonProvider provider = new JacksonJaxbJsonProvider();
provider.setMapper(yourObjectMapper());
Client client = ClientBuilder.newClient(new ClientConfig(provider));
Unfortunately in Jersey 2.26 they copied JacksonJaxbJsonProvider class
from com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider artifact (Jackson)
to org.glassfish.jersey.media:jersey-media-json-jackson artifact (Jersey)
and changed package
from com.fasterxml.jackson.jaxrs.json
to org.glassfish.jersey.jackson.internal.jackson.jaxrs.json.
It is still possible to use this approach it's just needed to change JacksonJaxbJsonProvider import.
Apart from JacksonJaxbJsonProvider being now in internal package drawback is also
that you must know on which Jersey version your code runs which might be a problem when different dependencies require different Jersey versions.
Better solution with ContextResolver<ObjectMapper>
Better possibility how to configure Jackson in Jersey client is to use the same way how it is configured in Jersey server which is to create ObjectMapper provider like this:
#Provider
#Produces(MediaType.APPLICATION_JSON)
public class ObjectMapperProvider implements ContextResolver<ObjectMapper> {
private ObjectMapper objectMapper = yourObjectMapper();
#Override
public ObjectMapper getContext(Class<?> objectType) {
return objectMapper;
}
}
and use it for example like this:
ClientConfig clientConfig = new ClientConfig();
clientConfig.register(JacksonFeature.class); // usually auto-discovered
clientConfig.register(new ObjectMapperProvider());
Client client = ClientBuilder.newClient(clientConfig);
If you have both the server and the client you can reuse ObjectMapperProvider class.
It seems that this approach works from Jersey version 2.9.
You might also want to try org.codehaus.jackson.jaxrs.JacksonJaxbJsonProvider (jackson-jaxrs 1.6.1).
I ran into similar issue, but for me none of the suggestions given here worked.
What worked for me was below piece of code:
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Client;
...
ClientBuilder clientBuilder = ClientBuilder.newBuilder()
clientBuilder.register(JacksonFeature.class);
...
Client client = clientBuilder.build();
The key change was usage of JacksonFeature.class - it comes from jersey-media-json-jackson-x.yy.jar
I got clue to use this solution from this article - http://www.baeldung.com/jersey-jax-rs-client
For jersey 2.22.2 and Jackson 2.7.2 gradle dependencies are:
dependencies {
compile("com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider:2.7.2")
compile("org.glassfish.jersey.core:jersey-client:2.22.2")
}
Sample client code is:
final String name = "world";
final Client client = ClientBuilder.newClient().register(JacksonJaxbJsonProvider.class);
final WebTarget target = client.target("http://localhost:8080").path("hello").path(name);
final Message message = target.request().get(Message.class);
System.out.println(message.getWelcomeMessage()); // hello world

Resources