I want to move my Circuit Breaker Configuration from application.yml file to some config java file as bean declaration beacuse it makes application.yml file to be large, Will it be possible for me to remove the configuration from applciation.yml and use configuration annotation to define circuit breaker configuration.
I have config java file with following code like :
#Configuration
#Component
public class CircuitBreakConfig {
#Bean
public CircuitBreaker defaultCircuitBreaker() {
CircuitBreakerConfig circuitBreakerConfig = CircuitBreakerConfig.custom()
.failureRateThreshold(50)
.slidingWindowSize(5)
.waitDurationInOpenState(Duration.ofMillis(6000))
.recordExceptions(IOException.class, TimeoutException.class,DataIntegrityViolationException.class)
.build();
// Create a CircuitBreakerRegistry with a custom global configuration
CircuitBreakerRegistry circuitBreakerRegistry =
CircuitBreakerRegistry.of(circuitBreakerConfig);
// Get or create a CircuitBreaker from the CircuitBreakerRegistry
// with the global default configuration
return circuitBreakerRegistry.circuitBreaker("default");
}
}
In My service file I am annotating method for circuit breaker like
#CircuitBreaker(name = "default", fallbackMethod = "fallback")
public Employee createEmployeeDefinition(Employee emp) throws Exception{
if(Objects.nonNull(sample)){
return EmployeeRepository.save(sample);
}
return null;
}
public Employee fallback(Employee emp,Exception ex) throws Exception{
System.out.println(ex.getClass()+" "+ex.getMessage() );
return null;
}
With this current implementation , I am unable to reach open state in my circuit breaker.Kindly provide me some suggestions
Please set minimumNumberOfCalls property
Related
I'm using the following dependency to send and receive messages from a azure service bus topic:
<dependency>
<groupId>com.azure.spring</groupId>
<artifactId>spring-cloud-azure-starter-servicebus-jms</artifactId>
<version>4.2.0</version>
</dependency>
I'd like to create the configuration via code through a spring bean because I need to configure more than 1 connection string, so after read the documentation, I decided to create this bean:
#Bean
#Primary
public AzureServiceBusJmsProperties priceListJmsProperties() {
var properties = new AzureServiceBusJmsProperties();
properties.setConnectionString(connectionString);
properties.setPricingTier("standard");
properties.setTopicClientId(priceListTopicName);
return properties;
}
If I debug the object creation, I see that this object is been creating twice, the first one with the configuration that I've provided, and the second one with null data, and this is the reason of why I'm getting the following error because there is a validation in this object that throws an exception if certain field is not set in the properties file:
spring.jms.servicebus.connection-string' should be provided
I've tried creating a connection factory instead but for the reason above, I'm getting the same error.
Anyone knows how I can set this configuration as a bean instead of the application.properties file? Thanks in advance.
Following the #DeepDave-MT answer, I couldn't disable the jms autoconfiguration with the spring.jms.servicebus.enabled property, so I decided to exclude the ServiceBusJmsAutoConfiguration with the property spring.autoconfigure.exclude, you have to pass the package name to this property.
Then, in my config class, I just added the following beans:
#Bean
#Primary
public ConnectionFactory connectionFactory() {
var connectionFactory = new ServiceBusJmsConnectionFactory(connectionString);
var serviceBusConnectionString = new ServiceBusConnectionString(connectionString);
var remoteUri = String.format(AMQP_URI_FORMAT, serviceBusConnectionString.getEndpointUri(), 100000);
connectionFactory.setRemoteURI(remoteUri);
connectionFactory.setClientID(topicName);
connectionFactory.setUsername(serviceBusConnectionString.getSharedAccessKeyName());
connectionFactory.setPassword(serviceBusConnectionString.getSharedAccessKey());
return new CachingConnectionFactory(connectionFactory);
}
#Bean
#Primary
public JmsListenerContainerFactory<?> topicJmsListenerContainerFactory(#Qualifier("connectionFactory") ConnectionFactory connectionFactory) {
var topicFactory = new DefaultJmsListenerContainerFactory();
topicFactory.setConnectionFactory(connectionFactory);
topicFactory.setSubscriptionDurable(Boolean.TRUE);
topicFactory.setErrorHandler(priceListErrorHandler());
return topicFactory;
}
#Bean
#Primary
public AzureServiceBusJmsProperties jmsProperties() {
var properties = new AzureServiceBusJmsProperties();
properties.setConnectionString(connectionString);
properties.setPricingTier("standard");
properties.setTopicClientId(topicName);
return properties;
}
I am trying to use the RepliyngKafkaTemplate like I managed to use the KafkaTemplate in a REST controller.
#RestController
public class TestController {
#Autowired
private ReplyingKafkaTemplate<Object, KafkaExampleRecord, KafkaExampleRecord> replyingTemplate;
#PostMapping("/test/request")
public void requestReply(#RequestBody KafkaExampleRecord record) throws ExecutionException, InterruptedException, TimeoutException {
ProducerRecord<Object, KafkaExampleRecord> producerRecord = new ProducerRecord<>("mytopic", record);
RequestReplyFuture<Object, KafkaExampleRecord, KafkaExampleRecord> replyFuture = replyingTemplate.sendAndReceive(producerRecord);
SendResult<Object, KafkaExampleRecord> sendResult = replyFuture.getSendFuture().get(10, TimeUnit.SECONDS);
ConsumerRecord<Object, KafkaExampleRecord> consumerRecord = replyFuture.get(10, TimeUnit.SECONDS);
}
}
However I am getting the following exception.
Field replyingTemplate in com.blah.KafkaController required a bean of type 'org.springframework.kafka.requestreply.ReplyingKafkaTemplate' that could not be found.
I enabled auto configuration like this.
#Configuration
#EnableKafka
public class KafkaConfig {
}
All Kafka settings are in my application.yml.
What else do I need? Do I really have to define beans? Seems unnecessary.
Do I really have to define beans? Seems unnecessary.
Yes, you have to declare a beans for the replying template (including the reply container); Spring Boot only auto configures a simple KafkaTemplate.
Can you check, whether you are scanning the basePackages correctly. Sometimes, you may end-up with this issue, if you not scanning the packages correctly, and I have experienced this many times in the Spring Boot application.
#ComponentScan(
basePackages = {
"x.x.x.x"
}
)
I've enabled Caching in my Spring Boot app and I use Redis to serve the purpose.
My Spring Configuration is:
#Configuration
#EnableCaching
#ConditionalOnProperty(
value = "spring.cache.enabled",
matchIfMissing = true)
public class CacheConfiguration {
#Bean
public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
return RedisCacheManager.builder(redisConnectionFactory)
.cacheDefaults(buildDefault())
.withInitialCacheConfigurations(buildFromSettings())
.transactionAware()
.build();
}
private Map<String, RedisCacheConfiguration> buildFromSettings() {
return Arrays.stream(CacheSettings.values()).collect(Collectors.toMap(CacheSettings::getCacheName, this::buildFromSettings));
}
private RedisCacheConfiguration buildDefault() {
return RedisCacheConfiguration.defaultCacheConfig()
.prefixCacheNameWith(CacheSettings.DEFAULTS.getCacheName())
.entryTtl(Duration.ofSeconds(CacheSettings.DEFAULTS.getTtl()));
}
private RedisCacheConfiguration buildFromSettings(CacheSettings cacheSettings) {
return RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofSeconds(cacheSettings.getTtl()));
}
}
However, whenever any problems with Redis server happens, for example timeout exception occurs, the app stops working whereas I think it had better skip the Caching and go on with normal execution flow.
So, does anyone have any idea on how to do it in Spring Boot?
Here is the exception I got
org.springframework.dao.QueryTimeoutException: Redis command timed out; nested exception is io.lettuce.core.RedisCommandTimeoutException
I know, that I can simply extend CachingConfigurerSupport and only override the errorHandler() method, returning a custom CacheErrorHandler how to mentioned in this answer https://stackoverflow.com/a/68072419/1527469
But it works only if setting RedisCacheManager transactionAware to false
as mentioned in this answer https://stackoverflow.com/a/66010262/5837557
So, my question is how to skip the Caching in Spring Boot with configuration settings RedisCacheManager transactionAware to true?
I have the following configuration with which I create circuit breakers at runtime:
#Configuration
public class CircuitBreakerConfiguration
{
public final static String DEFAULT_CIRCUIT_BREAKER_REGISTRY = "DEFAULT_CIRCUIT_BREAKER_REGISTRY";
private CircuitBreakerConfig getCircuitBreakerConfig()
{
return CircuitBreakerConfig.custom()
.failureRateThreshold(10)
.waitDurationInOpenState(Duration.ofMillis(30000))
.permittedNumberOfCallsInHalfOpenState(2)
.slidingWindowType(CircuitBreakerConfig.SlidingWindowType.COUNT_BASED)
.slidingWindowSize(5)
.automaticTransitionFromOpenToHalfOpenEnabled(true)
.recordExceptions(CheckAvailabilityException.class)
.build();
}
#Bean
#Qualifier(DEFAULT_CIRCUIT_BREAKER_REGISTRY)
public CircuitBreakerRegistry getCircuitBreakerRegistry()
{
return CircuitBreakerRegistry.of(getCircuitBreakerConfig());
}
}
I want to move these configurations to my application.properties file.
I tried the following to override the default configs:
resilience4j.circuitbreaker.configs.default.sliding-window-size=10
resilience4j.circuitbreaker.configs.default.sliding-window-type=COUNT_BASED
resilience4j.circuitbreaker.configs.default.failure-rate-threshold=50
resilience4j.circuitbreaker.configs.default.wait-duration-in-open-state=30s
resilience4j.circuitbreaker.configs.default.permitted-number-of-calls-in-half-open-state=2
resilience4j.circuitbreaker.configs.default.automatic-transition-from-open-to-half-open-enabled=true
resilience4j.circuitbreaker.configs.default.record-exceptions=com.example.web.domain.checkavailability.exceptions.CheckAvailabilityException
However, this doesn't seem to override the default configs too.
Don't know if this is still an open topic, but I was struggling with a similar question and managed to find this article which offers some guidance: https://heapsteep.com/13-circuit-breaker-resilience4j/
As such, here's what I've done:
create the default configs I want:
resilience4j.circuitbreaker.configs.default.register-health-indicator=true
resilience4j.circuitbreaker.configs.default.permitted-number-of-calls-in-half-open-state=3
resilience4j.circuitbreaker.configs.default.sliding-window-type=TIME_BASED
resilience4j.circuitbreaker.configs.default.minimum-number-of-calls=50
create a instance that I will use in the annotation, and set up it like so:
resilience4j.circuitbreaker.instances.myInstance.baseConfig=default
annotate your method:
#Override
#CircuitBreaker(name = "myInstance", fallbackMethod = "fallbackmethod")
public String getName(int ID) {
\\ ...
}
I have been resting this setup with the #Retry function, but I would assume that it works the same way.
So I want to change the validation messages used to validate a model through a DropWizard resource.
I'm using java bean validation annotations. For example here is one of the fields I want to validate:
#NotEmpty(message = "Password must not be empty.")
I can test this works as expected using a validator.
However when I use DropWizard to do the validation on the resource it adds some extra stuff to that message. What I see is this - password Password must not be empty. (was null) and I've found the code that does this here - https://github.com/dropwizard/dropwizard/blob/master/dropwizard-validation/src/main/java/io/dropwizard/validation/ConstraintViolations.java
Specifically this method -
public static <T> String format(ConstraintViolation<T> v) {
if (v.getConstraintDescriptor().getAnnotation() instanceof ValidationMethod) {
final ImmutableList<Path.Node> nodes = ImmutableList.copyOf(v.getPropertyPath());
final ImmutableList<Path.Node> usefulNodes = nodes.subList(0, nodes.size() - 1);
final String msg = v.getMessage().startsWith(".") ? "%s%s" : "%s %s";
return String.format(msg,
Joiner.on('.').join(usefulNodes),
v.getMessage()).trim();
} else {
return String.format("%s %s (was %s)",
v.getPropertyPath(),
v.getMessage(),
v.getInvalidValue());
}
}
Is there any way I can override this behaviour? I just want to display the message that I set in the annotation...
Here is a programmatic solution in dropwizard 0.8:
public void run(final MyConfiguration config, final Environment env) {
AbstractServerFactory sf = (AbstractServerFactory) config.getServerFactory();
// disable all default exception mappers
sf.setRegisterDefaultExceptionMappers(false);
// register your own ConstraintViolationException mapper
env.jersey().register(MyConstraintViolationExceptionMapper.class)
// restore other default exception mappers
env.jersey().register(new LoggingExceptionMapper<Throwable>() {});
env.jersey().register(new JsonProcessingExceptionMapper());
env.jersey().register(new EarlyEofExceptionMapper());
}
I think it's more reliable than a config file. And as you can see it also enables back all other default exception mappers.
ConstraintViolationExceptionMapper is the one which uses that method. In order to override it, you need to deregister it and register your own ExceptionMapper.
Remove the exception mapper(s)
Dropwizard 0.8
Add the following to your yaml file. Note that it will remove all the default exception mappers that dropwizard adds.
server:
registerDefaultExceptionMappers: false
Dropwizard 0.7.x
environment.jersey().getResourceConfig().getSingletons().removeIf(singleton -> singleton instanceof ConstraintViolationExceptionMapper);
Create and add your own exception mapper
public class ConstraintViolationExceptionMapper implements ExceptionMapper<ConstraintViolationException> {
#Override
public Response toResponse(ConstraintViolationException exception) {
// get the violation errors and return the response you want.
}
}
and add your exception mapper in your application class.
public void run(T configuration, Environment environment) throws Exception {
environment.jersey().register(ConstraintViolationExceptionMapper.class);
}
#ValidationMethod should be useful here. isn't it?
http://www.dropwizard.io/0.9.0/docs/manual/validation.html
#ValidationMethod(message="Password cannot be empty")
#JsonIgnore
public boolean isPasswordProvided() {
return false if password not provided;
}