Multiple Redis For Spring Cache And JWT - spring

I have actually an application that is using Redis for Cache and keeping the JWT token into redis.
My goal is to have one redis for cache, and another one for jwt token.
I dont understand how i can achieve this.
How can i say to spring to use a specific redis (here "redis-cache") for caching ?
Actually i only put #EnableCaching and it is working properly
Thanks for any help
spring:
redis:
port: 7000
password: password123
host: 127.0.0.1
redis-cache: # New One
port: 7001
password: password123
host: 127.0.0.1
I'm using redisTemplate to keep jwt token into redis
#Configuration
public class GenericBeanConfig {
#Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
Jackson2JsonRedisSerializer<String> jrs = new Jackson2JsonRedisSerializer<String>(String.class);
template.setKeySerializer(jrs);
template.setConnectionFactory(connectionFactory);
return template;
}
}
...
#EnableCaching
public class ProjectsApplication {
public static void main(String[] args) {
SpringApplication.run(ProjectsApplication .class, args);
}
}
Caching some endpoint
#Cacheable(value = "users-rbac")
public UserResponseDTO search(#PathVariable String email) {
return userService.search(email);
}

You can define your custom CacheManager as well, you define your cache manager as follows. Customize this as you see, for now host, port and password are fixed but you can read them from application config file using #Value .
#Bean
public CacheManager cacheManager() {
// create redis configuration
RedisStandaloneConfiguration configuration =
new RedisStandaloneConfiguration("127.0.0.1", 7001);
configuration.setPassword(RedisPassword.of("password"));
// create a connection factory, use other connection factory if you want
LettuceConnectionFactory factory = new LettuceConnectionFactory(configuration);
factory.afterPropertiesSet();
RedisCacheWriter writer = RedisCacheWriter.nonLockingRedisCacheWriter(factory);
// define cache cnfiguration
RedisCacheConfiguration cacheConfiguration = RedisCacheConfiguration.defaultCacheConfig();
// use Jackson serdes do not use JDK one
cacheConfiguration.serializeValuesWith(
SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));
// create cache manager
return new RedisCacheManager(writer, cacheConfiguration);
}

Related

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

How to enable distributed/clustered cache when using redis with spring data cache

How to enable distributed/clustered cache when using Redis with spring-boot cache.
Especially when using Redis through spring-boot-starter-data-redis
Enable caching in the spring boot app is very simple. You would need to just follow three steps.
Define cache configuration
Add EnableCaching to any configuration class
Provide a CacheManager bean
For Redis, we've RedisCacheManager that can be configured and created.
Cache Configuration
#Configuration
#Getter
#Setter
#ConfigurationProperties(prefix = "cache")
public class CacheConfigurationProperties {
// Redis host name
private String redisHost;
// Redis port
private int redisPort;
// Default TTL
private long timeoutSeconds;
// TTL per cache, add enties for each cache
private Map<String, Long> cacheTtls;
}
Set their values via properties or yaml file like
cache.redisHost=localhost
cache.redisPort=6379
cache.timeoutSeconds=1000
cache.cacheTtls.cach1=100
cache.cacheTtls.cach2=200
Once you have created configuration, you can create cache config for RedisCacheManger by builder.
#Configuration
#EnableCaching
public class CacheConfig {
private static RedisCacheConfiguration createCacheConfiguration(long timeoutInSeconds) {
return RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofSeconds(timeoutInSeconds));
}
#Bean
public LettuceConnectionFactory redisConnectionFactory(CacheConfigurationProperties properties) {
RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
redisStandaloneConfiguration.setHostName(properties.getRedisHost());
redisStandaloneConfiguration.setPort(properties.getRedisPort());
return new LettuceConnectionFactory(redisStandaloneConfiguration);
}
#Bean
public RedisCacheConfiguration cacheConfiguration(CacheConfigurationProperties properties) {
return createCacheConfiguration(properties.getTimeoutSeconds());
}
#Bean
public CacheManager cacheManager(
RedisConnectionFactory redisConnectionFactory, CacheConfigurationProperties properties) {
Map<String, RedisCacheConfiguration> cacheConfigurations = new HashMap<>();
for (Entry<String, Long> cacheNameAndTimeout : properties.getCacheTtls().entrySet()) {
cacheConfigurations.put(
cacheNameAndTimeout.getKey(), createCacheConfiguration(cacheNameAndTimeout.getValue()));
}
return RedisCacheManager.builder(redisConnectionFactory)
.cacheDefaults(cacheConfiguration(properties))
.withInitialCacheConfigurations(cacheConfigurations)
.build();
}
}
If you're using Redis cluster than update cache properties as per that. In this some beans would become primary if you want cache specific bean than make these methods private.

Redis cache docker container with spring boot not working on my local Machine

I am using redis cache on my local Machine as a docker image. I have enabled the cache for one of my method using the cacheable anothion. Application is not able to cache the same when i am using on aws it is working instead of localhost
public class RedisConfig {
#Autowired
private JedisConnectionFactory jedisConnectionFactory;
#Bean
public RedisTemplate<Object, Object> redisTemplate() {
System.out.println("localhost")
System.out.println("6379");
jedisConnectionFactory.getHostName();
jedisConnectionFactory.getPort();
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(jedisConnectionFactory);
template.setValueSerializer(new GenericToStringSerializer<Object>(Object.class));
return template;
}
#Bean
public CacheManager cacheManager(RedisTemplate redisTemplate) {
RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate);
// Number of seconds before expiration. Defaults to unlimited (0)
cacheManager.setDefaultExpiration(60);
cacheManager.setUsePrefix(true);
return cacheManager;
}
}
#Cacheable(value="sgcode" , cacheManager ="cacheManager")
public String getSegmentCode(String aname ) {
Logger.info("code ", "##### SEGMENT METHOD CALLED ##### {}", aname);
return segmentCodeMap.get(aname);
}
Logger line will be printed only once after that it should fetch from cache.
Finally after lot of struggle found the root cause. use the enablecaching anotation in application class

Collisions may occur when using Spring #Cacheable and SimpleKeyGenerator

When I use #Cacheable and call different method with same parameter, it generated a same key.
SimpleKeyGenerator generated key without cache names.
I use spring-boot 1.3.2 with spring 4.2.4.
Here is a sample:
#Component
public static class CacheableTestClass {
#Cacheable(cacheNames = "test-cacheproxy-echo1")
public String echo1(String text) {
return text;
}
#Cacheable(cacheNames = "test-cacheproxy-echo2")
public String echo2(String text) {
return "Another " + text;
}
}
And run a test:
assertEquals("OK", cacheableTestClass.echo1("OK"));
assertEquals("Another OK", cacheableTestClass.echo2("OK")); // Failure: expected 'Another OK', actual 'OK'.
So, is there a way to resolve this issue?
Thanks a lot.
Update
Here is my CacheManager configuration.
#Bean
#ConditionalOnMissingBean(name = "cacheRedisTemplate")
public RedisTemplate<Object, Object> cacheRedisTemplate(
RedisConnectionFactory redisConnectionFactory)
throws UnknownHostException {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
template.setHashKeySerializer(template.getKeySerializer());
return template;
}
#Bean
public RedisCacheManager cacheManager(#Qualifier("cacheRedisTemplate") RedisTemplate<Object, Object> cacheRedisTemplate) {
RedisCacheManager cacheManager = new RedisCacheManager(cacheRedisTemplate);
cacheManager.setDefaultExpiration(
redisCacheProperties().getDefaultExpiration());
cacheManager.setExpires(redisCacheProperties().getExpires());
return cacheManager;
}
This has nothing to do with SimpleKeyGenerator but this is a redis-specific issue that does not use the name of the cache as a discriminant for the key it uses to store the value.
You need to invoke setUsePrefix(true) on your RedisCacheManager. This is what Spring Boot does when it auto-configures the cache manager for you. Note that it should have been the default and we're discussing how we can improve the out-of-the-box experience in a future release

Sending a message to an embedded HornetQ from an external application

I am using spring-boot 1.2.2.
I have an embedded hornet queue setup in application.properties:
spring.hornetq.mode=embedded
spring.hornetq.embedded.enabled=true
spring.hornetq.embedded.queues=myQueue
I want to add a message to "myQueue" from an external application (not the one with the embedded queue). Is this possible?
In the other application (the one without the embedded hornetq), I tried creating a connectionFactory that points to the embedded hornetq server but I don't really know what port I should be using. According to the spring-boot documentation it says it is only valid for "native" mode.
spring.hornetq.mode= # connection mode (native, embedded)
spring.hornetq.host=localhost # hornetQ host (native mode)
spring.hornetq.port=5445 # hornetQ port (native mode)
here is my code so far:
#EnableJms
#Configuration
public class HornetQConfig {
#Bean
public CachingConnectionFactory connectionFactory() {
CachingConnectionFactory cachingConnectionFactory =
new CachingConnectionFactory();
cachingConnectionFactory.setSessionCacheSize(10);
cachingConnectionFactory.setCacheProducers(false);
cachingConnectionFactory.setTargetConnectionFactory(hornetQConnectionFactory());
return cachingConnectionFactory;
}
#Bean
public HornetQConnectionFactory hornetQConnectionFactory() {
HornetQConnectionFactory connectionFactory =
new HornetQConnectionFactory(false, transportConfiguration());
return connectionFactory;
}
#Bean
public TransportConfiguration transportConfiguration() {
Map<String, Object> map = new HashMap<String, Object>();
map.put("host", "localhost");
map.put("port", 5445);
TransportConfiguration configuration =
new TransportConfiguration(
"org.hornetq.core.remoting.impl.netty.NettyConnectorFactory", map);
return configuration;
}
}
And then:
#Autowired
private JmsTemplate jmsTemplate;
#Scheduled(fixedDelay = 1000L)
public void send() {
this.jmsTemplate.convertAndSend("myQueue", "Hello from external app");
}
But I am getting a connection problem.
Failed to create session factory; nested exception is HornetQNotConnectedException[errorType=NOT_CONNECTED message=HQ119007: Cannot connect to server(s)
The issue is that the embedded HornetQ server is configured with only an InVMAcceptorFactory by default. You need to add an AcceptorFactory that actually listens on a port, like NettyAcceptorFactory.
You can use the HornetQConfigurationCustomizer to configure this. Below example uses a hardcoded host/port, but you can easily create your own properties to make this configurable.
#Bean
public HornetQConfigurationCustomizer hornetCustomizer() {
return new HornetQConfigurationCustomizer() {
#Override
public void customize(Configuration configuration) {
Set<TransportConfiguration> acceptors = configuration.getAcceptorConfigurations();
Map<String, Object> params = new HashMap<String, Object>();
params.put("host", "localhost");
params.put("port", "5445");
TransportConfiguration tc = new TransportConfiguration(NettyAcceptorFactory.class.getName(), params);
acceptors.add(tc);
}
};
}
In your application with the embedded server, you configure it as embedded (as I believe you already have anyway, just to make sure):
spring.hornetq.mode=embedded
spring.hornetq.embedded.enabled=true
spring.hornetq.embedded.queues=myQueue
And in your "other" application that you want to connect to the embedded server, you configure HornetQ in native mode:
spring.hornetq.mode=native
spring.hornetq.host=localhost
spring.hornetq.port=5445

Resources