Spring Auto Configuration prioritization between Lettuce or Jedis - spring

I want to use Lettuce as a Redis Client, which is the default dependency for spring-boot-starter-data-redis-reactive. However I am inheriting Jedis as a dependency from another component written as pure Java code (no Spring). This is resulting in a conflict when initializing LettuceConnectionFactory due to presence of JedisConnectionFactory.
How can I keep Jedis in dependency for other component's use while ensuring that LettuceConnectionFactory is initialised for my own code? The main reason for using LetticeConnectionFactory is reactive programming in my service.
Both the connection factories are configured for initialization via RedisAutoConfiguration with no option of prioritisation.
https://github.com/spring-projects/spring-boot/blob/master/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/RedisAutoConfiguration.java

You could override RedisConnectionFactory by creating your own #Configuration class and adding new #Bean in it.
For example:
#Bean
RedisConnectionFactory myLettuceConnectionFactory() {
// your setup....
new LettuceConnectionFactory();
}
and then use myLettuceConnectionFactory bean to setup RedisTemplate #Bean
#Bean
public RedisTemplate<String, Object> redisTemplate() {
final RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(myLettuceConnectionFactory());
// other settings...
return template;
}

Related

how to set the expiry time for redis cache in springboot

Hi I want to add an expiry time to the redis cache.
I have added spring.cache.redis.time-to-live=1m.
, but its not working.
I am using Jedis connection factory , for connection
please some thing , new to Redis.
Tried to create some beans as I got from google, but not working.
To add an expiry time to Redis cache in Spring Boot, you can set the "spring.cache.redis.time-to-live" property in the application.properties file as you have done. This property specifies the default time-to-live for cache entries.
However, there are a few things to check if it's not working as expected:
Ensure that you have the Spring Boot Redis dependency in your pom.xml or build.gradle file.
Check if the Redis cache is enabled by adding the #EnableCaching annotation to your Spring Boot application class.
Check if the RedisConnectionFactory is properly configured. In your case, you are using JedisConnectionFactory. You can create a bean for the RedisConnectionFactory as follows:
#Bean
public RedisConnectionFactory redisConnectionFactory() {
JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory();
// set the Redis server host and port
jedisConnectionFactory.setHostName("localhost");
jedisConnectionFactory.setPort(6379);
jedisConnectionFactory.afterPropertiesSet();
return jedisConnectionFactory;
}
Ensure that the RedisTemplate is properly configured. You can create a bean for RedisTemplate as follows:
#Bean
public RedisTemplate<String, Object> redisTemplate() {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory());
// set the key and value serializer
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
Note that in the above example, I'm using the GenericJackson2JsonRedisSerializer for serializing the values. You can change this to any other serializer that suits your needs.
Once you have the RedisConnectionFactory and RedisTemplate beans configured properly, Spring Boot should automatically apply the time-to-live property to the cached values.
If the issue still persists, you can try setting the TTL for a specific cache using the #Cacheable annotation as follows:
#Cacheable(value = "myCache", key = "#key", unless = "#result == null", cacheManager = "cacheManager", cacheManagerCustomizers = MyCacheManagerCustomizer.class)
public Object getCachedData(String key) {
// your code to retrieve the data from the database or any other source
}
In the above example, I have set the TTL for the "myCache" cache using the MyCacheManagerCustomizer class. You can implement this class to customize the cache manager as per your requirements.
I hope this helps you to add an expiry time to Redis cache in your Spring Boot application.

Connection pooling not working in Redis Template

I am pretty new to Spring and unable to understand bean concept properly. In the following code, if I make a bean of redisConnectionFactory(), redisTemplate is able to set the connection and connection pooling properly. If I don't create a bean of it, connection pooling doesn't work.
Since, I am setting connection in redisTemplate and creating a bean of it, shouldn't it work as expected?
I tried to look up for any answer, but could not get any answers for it. Any help would be really appreciated.
#Bean(name = "contactsRedisTemplate")
public RedisTemplate<String, Object> redisTemplate() {
RedisTemplate<String, Object> redisTemplate = redisHandler().getTemplate();
redisTemplate.setConnectionFactory(redisConnectionFactory());
return redisTemplate;
}
public RedisConnectionFactory redisConnectionFactory() {
RedisConnectionConfig redisConnectionConfig = new RedisConnectionConfig(redisConectionPoolSize, minIdleConnectionsCount,
maxIdleConnectionsCount, connectTimeoutMillis, readTimeoutMillis);
RedisAddress redisAddress = new RedisAddress(redisHost, redisPort, databaseIndex, redisPassword);
RedisConnectionFactory jedisConnectionFactory = getJedisConnectionFactory(redisAddress, redisConnectionConfig);
return jedisConnectionFactory;
}

Java JobRunr when using Spring Boot Redis Starter

How do I create and use the Redis connection that spring-boot-starter-data-redis creates? It doesn't seem like there is a Bean for RedisClient created by the default auto configuration so I'm not sure of the best way to do this.
The documentation does state that in this case you need to create the StorageProvider yourself which is fine, but can you reuse what Spring Boot has already created. I believe this would need to be a pooled connection which you would also need to enable through Spring Boot.
RedisTemplate offers a high-level abstraction for Redis interactions:
https://docs.spring.io/spring-data/data-redis/docs/current/reference/html/#redis:template
Redis autoconfiguration :
#AutoConfiguration
#ConditionalOnClass({RedisOperations.class})
#EnableConfigurationProperties({RedisProperties.class})
#Import({LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class})
public class RedisAutoConfiguration {
public RedisAutoConfiguration() {
}
#Bean
#ConditionalOnMissingBean(
name = {"redisTemplate"}
)
#ConditionalOnSingleCandidate(RedisConnectionFactory.class)
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<Object, Object> template = new RedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
#Bean
#ConditionalOnMissingBean
#ConditionalOnSingleCandidate(RedisConnectionFactory.class)
public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
return new StringRedisTemplate(redisConnectionFactory);
}
}
Here you can find the corresponding configuration properties(including connection pool default configuration).
Simple implementation example :
https://www.baeldung.com/spring-data-redis-tutorial

Spring Integration - kafka Outbound adapter not taking topic value exposed as spring bean

I have successfully integrated kafka outbound channle adapter with fixed topic name. Now, i want to make the topic name configurable and hence, want to expose it via application properties.
application.properties contain one of the following entry:
kafkaTopic:testNewTopic
My configuration class looks like below:
#Configuration
#Component
public class KafkaConfig {
#Value("${kafkaTopic}")
private String kafkaTopicName;
#Bean
public String getTopic(){
return kafkaTopicName;
}
#Bean
public KafkaTemplate<String, String> kafkaTemplate() {
return new KafkaTemplate<>(producerFactory());
}
#Bean
public ProducerFactory<String, String> producerFactory() {
Map<String, Object> props = new HashMap<>();
props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");//this.brokerAddress);
props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringSerializer");
props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringSerializer");
// set more properties
return new DefaultKafkaProducerFactory<>(props);
}
}
and in my si-config.xml, i have used the following (ex: topic="getTopic") :
<int-kafka:outbound-channel-adapter
id="kafkaOutboundChannelAdapter" kafka-template="kafkaTemplate"
auto-startup="true" sync="true" channel="inputToKafka" topic="getTopic">
</int-kafka:outbound-channel-adapter>
However, the configuration is unable to pick up the topic name when exposed via bean. But it works fine when i hard code the value of the topic name.
Can someone please suggest what i am doing wrong here?
Does topic within kafka outbound channel accept the value referred as bean?
How do i externalize it as every application using my utility will supply different kafka topic names
The topic attribute is for string value.
However it supports property placeholder resolution:
topic="${kafkaTopic}"
and also SpEL evaluation for aforementioned bean:
topic="#{getTopic}"
Just because this is allowed by the XML parser configuration.
However you may pay attention that KafkaTemplate, which you inject into the <int-kafka:outbound-channel-adapter> has defaultTopic property. Therefore you won't need to worry about that XML.
And one more option available for you is Spring Integration Annotations configuration. Where you can define a #ServiceActivator for the KafkaProducerMessageHandler #Bean:
#ServiceActivator(inputChannel = "inputToKafka")
#Bean
KafkaProducerMessageHandler kafkaOutboundChannelAdapter() {
kafkaOutboundChannelAdapter adapter = new kafkaOutboundChannelAdapter( kafkaTemplate());
adapter.setSync(true);
adapter.setTopicExpression(new LiteralExpression(this.kafkaTopicName));
return adapter;
}

How to create a second RedisTemplate instance in a Spring Boot application

According to this answer, one RedisTemplate cannot support multiple serializers for values. So I want to create multiple RedisTemplates for different needs, specifically one for string actions and one for object to JSON serializations, to be used in RedisCacheManager. I'm using Spring Boot and the current RedisTemplate is autowired, I'm wondering what's the correct way to declare a second RedisTemplate instance sharing the same Jedis connection factory but has its own serializers?
Tried something like this in two different components,
Component 1 declares,
#Autowired
private RedisTemplate redisTemplate;
redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer(Instance.class));
Component 2 declares,
#Autowired
private StringRedisTemplate stringRedisTemplate;
In this case the two templates actually are the same. Traced into Spring code and found component 1's template got resolved to autoconfigured stringRedisTemplate.
Manually calling RedisTemplate's contructor and then its afterPropertiesSet() won't work either as it complains no connection factory can be found.
I know this request probably is no big difference from defining another bean in a Spring app but not sure with the current Spring-Data-Redis integration what's the best way for me to do. Please help, thanks.
you can follow two ways how to use multiple RedisTemplates within one Spring Boot application:
Named bean injection with #Autowired #Qualifier("beanname") RedisTemplate myTemplate and create the bean with #Bean(name = "beanname").
Type-safe injection by specifying type parameters on RedisTemplate (e.g. #Autowired RedisTemplate<byte[], byte[]> byteTemplate and #Autowired RedisTemplate<String, String> stringTemplate).
Here's the code to create two different:
#Configuration
public class Config {
#Bean
public RedisTemplate<String, String> stringTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, String> stringTemplate = new RedisTemplate<>();
stringTemplate.setConnectionFactory(redisConnectionFactory);
stringTemplate.setDefaultSerializer(new StringRedisSerializer());
return stringTemplate;
}
#Bean
public RedisTemplate<byte[], byte[]> byteTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<byte[], byte[]> byteTemplate = new RedisTemplate<>();
byteTemplate.setConnectionFactory(redisConnectionFactory);
return byteTemplate;
}
}
HTH, Mark

Resources