How Can I configure separate resilience4j circuit breaker for each instance registered under the SAME Eureka service - spring-cloud

Here is my scenario:
Eureka Server: MY-APP-SERVICE with 3 instances of same spring boot app running on e.g localhost:8080, localhost:8081 and localhost:8082
OpenFeign client (Annotated as follow)
#FeignClient(name="MY-APP-SERVICE")
public interface MyFeignClient {...}
I have a proxy controller e.g. MyAppServiceClientController as follow:
#RestController
public class MyAppServiceClientController {
Autowired
MyFeignClient myFeignClient;
#CircuitBreaker(name = "backendA")
public String doSomething() {
return myFeignClient.doSomething();
}
}
When FeignClient asks Eureka registry for the service MY-APP-SERVICE it gets all 3 instances; it seems like resilience4j has only 1 circuit breaker at service level, here MY-APP-SERVICE.
How can I configure/annotate my FeignClient to have a separate resilience4j circuit breaker for each instance/URL in my MY-APP-SERVICE ?

Related

How to share a kafka connector and its configuration between multiple services in Quarkus?

I've got the following project structure:
project
- serviceA
- serviceB
- serviceC
- serviceD
And I would like to have a common kafka connector in all the project services. Something like this:
public class CommonProcessor {
private final CommonService commonService;
public CommonProcessor(final CommonService commonService) {
this.commonService= commonService;
}
#Incoming("channel-name")
public CompletionStage<Void> process(final Message<CommonMessage> message) {
final CommonMessage commonMessage = message.getPayload();
commonService.commonMethod(commonMessage.getAttribute());
return message.ack();
}
}
With its corresponding configuration:
mp.messaging.incoming.channel-name.connector=smallrye-kafka
mp.messaging.incoming.channel-name.topic=channel-name
mp.messaging.incoming.channel-name.value.deserializer=foo.bar.CommonDeserializer
How could I share this CommonProcessor (and its configuration) between all services?
Can I put it in a common jar and add it as a dependency in all services? I've tried this option but it does not work because of the configuration. When starting the service with the dependency it says Impossible to bind mediators, some media
tors are not connected: [foo.bar.CommonProcessor#process]
Can it be done through an extension? In this case, I have the same question, how can I share the configuration in application.properties?

Redis Spring data with Lettuce: com.lambdaworks.redis.RedisCommandExecutionException: MOVED error

I'm using AWS ElastiCache (Redis) in cluster mode. I'm having two implementations to connect to ElastiCache. One of the implementations is directly using the native Lettuce driver and other using Spring data with Lettuce as underneath driver. AWS ElastiCache has Cluster configuration endpoint. I want to use this endpoint to connect to ElastiCache. I'm able to successfully connect to ElastiCache using cluster endpoint with native Lettuce driver implementation but getting below error when using spring data with cluster endpoint
Spring data: 1.8.9-RELEASE (Using a higher version of Spring is not an option)
Lettuce: 4.5.0-FINAL
#Bean
public LettuceConnectionFactory redisConnectionFactory() {
LettuceConnectionFactory lettuceConnectionFactory = new LettuceConnectionFactory();
lettuceConnectionFactory.setHostName(<cluster_endpoint>);
lettuceConnectionFactory.setPort();
lettuceConnectionFactory.setUseSsl(Boolean.valueOf(useSsl));
//lettuceConnectionFactory.setPassword(password);
return lettuceConnectionFactory;
}
Error:
Caused by: org.springframework.data.redis.RedisSystemException: Error in execution; nested exception is com.lambdaworks.redis.RedisCommandExecutionException: MOVED 12894 cache---.usw2.cache.amazonaws.com:6379
at org.springframework.data.redis.connection.lettuce.LettuceExceptionConverter.convert(LettuceExceptionConverter.java:50)
at org.springframework.data.redis.connection.lettuce.LettuceExceptionConverter.convert(LettuceExceptionConverter.java:48)
at org.springframework.data.redis.connection.lettuce.LettuceExceptionConverter.convert(LettuceExceptionConverter.java:41)
It works fine in below two scenarios -
1) I use cluster node endpoints and inject in LettuceConnectionFactory via RedisClusterConfiguration.
2) If I use Lettuce as direct implementation (not through Spring data) using StatefulRedisClusterConnection.
What could be the reason for the above two errors? I would prefer to use Lettuce with Spring data by using cluster configuration endpoint.
Below is the solution for connecting to Elasticache Redis cluster using Spring Data -
Using ElastiCache Cluster Configuration Endpoint:
#Bean
public RedisClusterConfiguration redisClusterConfiguration() {
RedisClusterConfiguration clusterConfiguration = new
RedisClusterConfiguration();
clusterConfiguration.clusterNode("host", port);
new LettuceConnectionFactory(clusterConfiguration);
}
Using ElastiCache Node endpoints:
#Bean
public RedisClusterConfiguration redisClusterConfiguration() {
RedisClusterConfiguration clusterConfiguration = new RedisClusterConfiguration()
.clusterNode("redis-cluster----0001-001.redis-cluster---.usw2.cache.amazonaws.com",6379)
.clusterNode("redis-cluster----0001-002.redis-cluster---.usw2.cache.amazonaws.com",6379)
.clusterNode("redis-cluster----0001-003.redis-cluster---.usw2.cache.amazonaws.com",6379)
.clusterNode("redis-cluster----0001-004.redis-cluster---.usw2.cache.amazonaws.com",6379);
return clusterConfiguration;
}
Thanks to Mark Paluch who responded to my issue in Spring Data forum.
Here is the detail - https://jira.spring.io/browse/DATAREDIS-898

loadbalanced ribbon client initialization against discovery service (eureka)

I have service which runs some init scripts after application startup (implemented with ApplicationListener<ApplicationReadyEvent>). In this scripts I need to call another services with RestTemplate which is #LoadBalanced. When the call to service is invoked there's no information about instances of remote service because discovery server was not contacted at that time (I guess).
java.lang.IllegalStateException: No instances available for api-service
at org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient.execute(RibbonLoadBalancerClient.java:79)
So is there way how to get list of available services from discovery server at application startup, before my init script will execute?
Thanks
edit:
The problem is more related to fact, that in current environment (dev) all services are tied together in one service (api-service). So from within api-service I'm trying to call #LoadBalanced client api-service which doesn't know about self? Can I register some listener or something similar to know when api-service (self) will be available?
here are the sample applications. I'm mainly interested how to have working this method
edit2:
Now there could be the solution to create EurekaListener
public static class InitializerListener implements EurekaEventListener {
private EurekaClient eurekaClient;
private RestOperations restTemplate;
public InitializerListener(EurekaClient eurekaClient, RestOperations restTemplate) {
this.eurekaClient = eurekaClient;
this.restTemplate = restTemplate;
}
#Override
public void onEvent(EurekaEvent event) {
if (event instanceof StatusChangeEvent) {
if (((StatusChangeEvent) event).getStatus().equals(InstanceInfo.InstanceStatus.UP)) {
ResponseEntity<String> helloResponse = restTemplate.getForEntity("http://api-service/hello-controller/{name}", String.class, "my friend");
logger.debug("Response from controller is {}", helloResponse.getBody());
eurekaClient.unregisterEventListener(this);
}
}
}
}
and then register it like this:
EurekaEventListener initializerListener = new InitializerListener(discoveryClient, restTemplate);
discoveryClient.registerEventListener(initializerListener);
However this is only executed only when application is registered to discovery service first time. Next time when I stop the api-service and run it again, event is not published. Is there any other event which can I catch?
Currently, in Camden and earlier, applications are required to be registered in Eureka before they can query for other applications. Your call is likely too early in the registration lifecycle. There is an InstanceRegisteredEvent that may help. There are plans to work on this in the Dalston release train.

Spring Cloud | Feign Hytrix | First Call Timeout

I have a service that has uses 3 feign clients. Each time I start my application, I get a TimeoutException on the first call to any feign client.
I have to trigger each feign client at least once before everything is stable. Looking around online, the problem is that something inside of feign or hystrix is lazy loaded and the solution was to make a configuration class that overrides the spring defaults. I've tried that wiith the below code and it is still not helping. I still see the same issue. Anyone know a fix for this? Is the only solution to call the feignclient twice via a hystrix callback?
#FeignClient(value = "SERVICE-NAME", configuration =ServiceFeignConfiguration.class)
#Configuration
public class ServiceFeignConfiguration {
#Value("${service.feign.connectTimeout:60000}")
private int connectTimeout;
#Value("${service.feign.readTimeOut:60000}")
private int readTimeout;
#Bean
public Request.Options options() {
return new Request.Options(connectTimeout, readTimeout);
}
}
Spring Cloud - Brixton.SR4
Spring Boot - 1.4.0.RELEASE
This is all running in docker
Ubuntu - 12.04
Docker - 1.12.1
Docker-Compose - 1.8
I found the solution to be that the default properties of Hystrix are not good. They have a very small timeout window and the request will always time out on the first try. I added these properties to my application.yml file in my config service and now all of my services can use feign with no problems and i dont have to code around the first time timeout
hystrix:
threadpool.default.coreSize: "20"
threadpool.default.maxQueueSize: "500000"
threadpool.default.keepAliveTimeMinutes: "2"
threadpool.default.queueSizeRejectionThreshold: "500000"
command:
default:
fallback.isolation.semaphore.maxConcurrentRequests: "20"
execution:
timeout:
enabled: "false"
isolation:
strategy: "THREAD"
thread:
timeoutInMilliseconds: "30000"

annotation #RibbonClient not work together with RestTemplate

I am trying Ribbon configuration with RestTemplate based on bookmark service example but without luck, here is my code:
#SpringBootApplication
#RestController
#RibbonClient(name = "foo", configuration = SampleRibbonConfiguration.class)
public class BookmarkServiceApplication {
public static void main(String[] args) {
SpringApplication.run(BookmarkServiceApplication.class, args);
}
#Autowired
RestTemplate restTemplate;
#RequestMapping("/hello")
public String hello() {
String greeting = this.restTemplate.getForObject("http://foo/hello", String.class);
return String.format("%s, %s!", greeting);
}
}
with error page as below:
Whitelabel Error Page
This application has no explicit mapping for /error, so you are seeing this as a fallback.
Tue Mar 22 19:59:33 GMT+08:00 2016
There was an unexpected error (type=Internal Server Error, status=500).
No instances available for foo
but if I remove annotation #RibbonClient, everything will be just ok,
#RibbonClient(name = "foo", configuration = SampleRibbonConfiguration.class)
and here is SampleRibbonConfiguration implementation:
public class SampleRibbonConfiguration {
#Autowired
IClientConfig ribbonClientConfig;
#Bean
public IPing ribbonPing(IClientConfig config) {
return new PingUrl();
}
#Bean
public IRule ribbonRule(IClientConfig config) {
return new AvailabilityFilteringRule();
}
}
Is it because RibbonClient can not work with RestTemplate together?
and another question is that does Ribbon configuration like load balancing rule could be configured via application.yml configuration file?
as from Ribbon wiki, seems we can configure Ribbon parameters like NFLoadBalancerClassName, NFLoadBalancerRuleClassName etc in property file, does Spring Cloud also supports this?
I'm going to assume you're using Eureka for Service Discovery.
Your particular error:
No instances available for foo
can happen for a couple of reasons
1.) All services are down
All of the instances of your foo service could legitimately be DOWN.
Solution: Try visiting your Eureka Dashboard and ensure all the services are actually UP.
If you're running locally, the Eureka Dashboard is at http://localhost:8761/
2.) Waiting for heartbeats
When you very first register a service via Eureka, there's a period of time where the service is UP but not available. From the documentation
A service is not available for discovery by clients until the
instance, the server and the client all have the same metadata in
their local cache (so it could take 3 heartbeats)
Solution: Wait a good 30 seconds after starting your foo service before you try calling it via your client.
In your particular case I'm going to guess #2 is likely what's happening to you. You're probably starting the service and trying to call it immediately from the client.
When it doesn't work, you stop the client, make some changes and restart. By that time though, all of the heartbeats have completed and your service is now available.
For your second question. Look at the "Customizing the Ribbon Client using properties" section in the reference documentation. (link)