Difference between #RibbonClient and #LoadBalanced - spring-boot

I understand #LoadBalanced indicates the Rest template should be based on Client Side Load Balancing using Ribbon and checks Eureka server for resolving the service name to host/port.
What is the use of #RibbonClient. Is it to support native Ribbon Client LB without Eureka and also support Eureka Discover when configured with DiscoveryEnabledNIWSServerList?

TL;DR: #LoadBalanced is a marker annotation & #RibbonClient is used for configuration purposes.
#LoadBalanced
Used as a marker annotation indicating that the annotated RestTemplate should use a RibbonLoadBalancerClient for interacting with your service(s).
In turn, this allows you to use "logical identifiers" for the URLs you pass to the RestTemplate. These logical identifiers are typically the name of a service. For example:
restTemplate.getForObject("http://some-service-name/user/{id}", String.class, 1);
where some-service-name is the logical identifier.
#RibbonClient
Used for configuring your Ribbon client(s).
Is #RibbonClient required?
No! If you're using Service Discovery and you're ok with all of the default Ribbon settings, you don't even need to use the #RibbonClient annotation.
When should I use #RibbonClient?
There are at least two cases where you need to use #RibbonClient
You need to customize your Ribbon settings for a particular Ribbon client
You're not using any service discovery
Customizing your Ribbon settings:
Define a #RibbonClient
#RibbonClient(name = "some-service", configuration = SomeServiceConfig.class)
name - set it to the same name of the service you're calling with Ribbon but need additional customizations for how Ribbon interacts with that service.
configuration - set it to an #Configuration class with all of your customizations defined as #Beans. Make sure this class is not picked up by #ComponentScan otherwise it will override the defaults for ALL Ribbon clients.
See the section "Customizing the RibbonClient` in the Spring Cloud Netflix documentation (link)
Using Ribbon without Service Discovery
If you're not using Service Discovery, the name field of the #RibbonClient annotation will be used to prefix your configuration in the application.properties as well as "logical identifier" in the URL you pass to RestTemplate.
Define a #RibbonClient
#RibbonClient(name = "myservice")
then in your application.properties
myservice.ribbon.eureka.enabled=false
myservice.ribbon.listOfServers=http://localhost:5000, http://localhost:5001

RestTemplate supports load balancing, using #LoadBalanced tells Spring Cloud that we want to take advantage of its load balancing support(If you are using Ribbon then the effect of using #LoadBalanced will be that RestTemplate will use RibbionClient to get the server address).
You can also check how LoadBalancerAutoConfiguration works here
Using #RibbonClients you can provide declarative configuration for a ribbon client.
E.g.
#SpringBootApplication
#RestController
#RibbonClient(name = "app", configuration = RibbonConfig.class)
public class App {
#LoadBalanced
#Bean
RestTemplate restTemplate(){
return new RestTemplate();
}
//...
}
Then you create RibbonConfig.class to override any Ribbon related bean.
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.IPing;
import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.PingUrl;
import com.netflix.loadbalancer.AvailabilityFilteringRule;
public class RibbonConfig {
#Autowired
IClientConfig ribbonClientConfig;
#Bean
public IPing ribbonPing (IClientConfig config) {
return new PingUrl();//we override default Iping which is a NoOpPing
}
#Bean
public IRule ribbonRule(IClientConfig config) {
return new AvailabilityFilteringRule(); // we override the default ZoneAvoidanceRule
}
}

Related

How to specify Ribbon Configuration for a specific Feign client in a Spring Boot project having multiple Feign clients?

I have a spring boot project which is having two Feign Client's.
First Feign client (i.e. ServiceAProxy.class below), which will work on Service Discovery using Eureka or Consul.
#FeignClient(name="service-A")
public interface ServiceAProxy{
#RequestMapping(method= RequestMethod.GET)
String getResponse();
}
Second Feign client( i.e ServiceBProxy.class ), needs to have a set of server list which will be picked from Spring Cloud Configuration server or Apache zookeeper config server and will be passed to the ribbon client of ServiceBProxy.class. For that, I'm trying to add a Ribbon Client Configuration that will modify the ribbon server list. Below is the code for the second feign client.
#FeignClient(name="service-B")
#RibbonClient(name="service-B",configuration= ServiceBProxy.LocalRibbonClientConfig.class)
public interface ServiceBProxy{
#RequestMapping(method = RequestMethod.GET)
String invalidateUsers();
#Configuration
class LocalRibbonClientConfig{
#Value("${server-host1}") // fetched from config
private String host1;
#Value("${server-host2}") // fetched from config
private String host2;
#Bean
public ServerList<Server> ribbonServerList() {
return new StaticServerList<>(
new Server(host1,8080),
new Server(host2,8081)
);
}
}
}
Now, when I run the code, ServiceBProxy.class works as expected and picks the list of servers that was specified in the LocalRibbonClientConfig.class.
But the problem is with the ServiceAProxy.class which was suppose to work on the basis of service discovery also starts to use the LocalRibbonClientConfig.
How can I only allow ServiceBProxy.class to use the custom ribbon configurations and other feign clients in the project to work as per their default behaviour.
Please guide on this.

Enable by default an Actuator Endpoint in Spring Boot

I developed a small library that adds a custom endpoint for the actuator and I like to expose it by default. Spring Boot 2.7.4 only exposes by default health.
At the moment, what I am doing is registering an EnvironmentPostProcessor to add a property to include health,jwks at the last PropertySource in the environment. But it seems a little bit fragile. There are other libraries that have to export other endpoints by default (metrics, prometheus...)
This is what I am doing at the moment:
public class PoCEnvironmentPostProcessor implements EnvironmentPostProcessor {
private static final String PROPERTY_NAME = "management.endpoints.web.exposure.include";
#Override
public void postProcessEnvironment(
ConfigurableEnvironment environment,
SpringApplication application
) {
var propertySources = environment.getPropertySources();
propertySources.stream()
.filter(it -> it.containsProperty(PROPERTY_NAME))
.findFirst().ifPresentOrElse(source -> {
var property = source.getProperty(PROPERTY_NAME);
var pocSource = new MapPropertySource(PROPERTY_NAME, Map.of(PROPERTY_NAME, property + ",jwks"));
// Add the new property with more priority
propertySources.addBefore(source.getName(), pocSource);
}, () -> {
var pocSource = new MapPropertySource(PROPERTY_NAME, Map.of(PROPERTY_NAME, "health,jwks"));
propertySources.addLast(pocSource);
});
}
}
Is there any way to expose by default that allow me to add several endpoints in different libraries without playing to much with the property sources?
It’s not exactly clear to me if you’re asking how the client apps that use your library would enable specific endpoints, or if you are writing more than one library and want to expose different endpoints. I’ll answer both.
management.endpoints.web.exposure.include=comma-separated-endpoints would enable the listed endpoints without your library having to do anything. Your client apps can set this property in application.yml.
If you want to set this property by default in your library, one of the easiest ways is to put it in a property file, and load it as a #PropertySource on a #Configuration bean. I’m assuming your library is a starter and the #Configuration bean is auto-configured. If you don’t know how to create a starter, refer to this article.

Use Micrometer with OpenFeign in spring-boot application

The OpenApi documentation says that it supports micrometer. How does the integration works? I could not find anything except this little documentation.
I have a FeignClient in a spring boot application
#FeignClient(name = "SomeService", url = "xxx", configuration = FeignConfiguration.class)
public interface SomeService {
#GET
#Path("/something")
Something getSomething();
}
with the configuration
public class FeignConfiguration {
#Bean
public Capability capability() {
return new MicrometerCapability();
}
}
and the micrometer integration as a dependency
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-micrometer</artifactId>
<version>10.12</version>
</dependency>
The code makes a call but I could not find any new metrics via the actuator overview, expecting some general information about my HTTP requests. What part is missing?
Update
I added the support for this to spring-cloud-openfeign. After the next release (2020.0.2), if micrometer is set-up, the only thing you need to do is putting feign-micrometer onto your classpath.
Old answer
I'm not sure if you do but I recommend to use spring-cloud-openfeign which autoconfigures Feign components for you. Unfortunately, it seems it does not autoconfigure Capability (that's one reason why your solution does not work) so you need to do it manually, please see the docs how to do it.
I was able to make this work combining the examples in the OpenFeign and Spring Cloud OpenFeign docs:
#Import(FeignClientsConfiguration.class)
class FooController {
private final FooClient fooClient;
public FooController(Decoder decoder, Encoder encoder, Contract contract, MeterRegistry meterRegistry) {
this.fooClient = Feign.builder()
.encoder(encoder)
.decoder(decoder)
.contract(contract)
.addCapability(new MicrometerCapability(meterRegistry))
.target(FooClient.class, "https://PROD-SVC");
}
}
What I did:
Used spring-cloud-openfeign
Added feign-micrometer (see feign-bom)
Created the client in the way you can see above
Importing FeignClientsConfiguration and passing MeterRegistry to MicrometerCapability are vital
After these, and calling the client, I had new metrics:
feign.Client
feign.Feign
feign.codec.Decoder
feign.codec.Decoder.response_size

How to determine which default http client was injected by Spring container for #FeignClient?

I am using #FeignClient annotation. I want to know which httpclient was injected by Spring when i am running my application.
For example,
The OkHttpClient and ApacheHttpClient feign clients can be used by setting feign.okhttp.enabled or feign.httpclient.enabled to true, respectively, and having them on the classpath.
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
#FeignClient(name = "service", path = "/api/v1", configuration = ServiceConfiguration.class)
public interface ServiceClient {
#RequestMapping(method = RequestMethod.GET, value = "/test/{param1}", consumes = MediaType.APPLICATION_JSON_VALUE)
String test(#PathVariable("param1") String param);
}
I am not sure now which of these client is being injected since my application is complex with multiple httpclient libraries in the classpath.
Is there a way that i can monitor it ?
I enabled JMX and tried to view the jconsole Mbeans and there was no information about httpclients.
It's impossible to tell without seeing your Spring setup, especially if your application is complex like you said.
Since you are using Spring annotations to declare your #FeignClient you most likely are depending on spring-cloud-netflix-core. If you are enabling defaults with #EnableFeignClients take a look at FeignAutoConfiguration class. This class registers the actual HTTP client bean. If you add both feign.httpclient.enabled and feign.okhttp.enabled properties (which IMO is a weird setup) try debugging FeignAutoConfiguration to see which Client feignClient() bean will be registered in the Spring context.
Alternatively enable wire logging in all HTTP client libraries and see which one actually executes the request based on logs.
According to previous answer, now I see this row in FeignAutoConfiguration.java
#ConditionalOnProperty(value = "feign.httpclient.enabled", matchIfMissing = true)
So simple answer would be Apache Client by default if you don't add any properties

How to call external non REST API from a spring cloud micro-service that use eureka to call other internal micro-service?

In our project we use Spring cloud + Eureka as service registry.
When we use the ribbon client to call internal micro-services, all URL are resolved via Eureka ... that's a problem to call external URLs.
As external API are old fashioned usage of Feign doesn't seem to be good choice.
What's the best way to call an external URL from such a service ?
Thanks in advance
Patrice
One way working:
Use two configurations.
Declare your RestTemplate Bean to call external services like this:
#Primary
#Qualifier("withoutEureka")
#Bean
public RestTemplate restTemplate(){
...
}
Inject this reference in your client this way
#Bean
public MyClientForExtCall myClientForExtCall(#Qualifier("withoutEureka")RestTemplate restTemplate)
In the other configuration use the restTemplate as usual, but don't forget to use another qualifier
#LoadBalanced
#Bean
#Qualifier("withEureka")
public RestTemplate loadBalancedEureka(){
...
}
#Bean
public MyClientForInternal myClientForInternal(#Qualifier("withoutEureka")RestTemplate restTemplate)
Patrice
You can use Ribbon without Eureka. For external APIs where you cannot configure in Eureka to abstract the discover. You can hard code their URLs in client and configure server list. The Ribbon client defaults to a configured server list, and you can supply the configuration like this:
stores:
ribbon:
listOfServers: example.com, google.com

Resources