One Spring Hibernateアプリケーションがあります。私のアプリケーションでは、最近、Spring data Redisを実装しています。
spring-servlet.xml
<!-- redis connection factory -->
<bean id="jedisConnFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" p:use-pool="true"/>
<!-- redis template definition -->
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate"
p:connection-factory-ref="jedisConnFactory"/>
そして、このredisTemplate
は私のServiceImplクラスで使用します。
RedisServiceImpl
@Autowired
private RedisTemplate<String, T> redisTemplate;
public RedisTemplate<String, T> getRedisTemplate() {
return redisTemplate;
}
public void setRedisTemplate(RedisTemplate<String, T> redisTemplate) {
this.redisTemplate = redisTemplate;
}
今、私はこのようにredisServerにデータを追加しました
public void putData(String uniqueKey, String key, Object results) {
redisTemplate.opsForHash().put(uniqueKey, key, results);
}
今、期限切れキーを削除したいです。
私はグーグルで検索しますが、グーグルではすべてこのように言っています
redisTemplate.expire(key, timeout, TimeUnit);
このexpireメソッドでは、uniqueKey
の代わりにkey
を提供する必要があります。しかし、key
の代わりにuniqueKey
を期限切れにする必要があります。
期限切れKey
に対して何ができますか?
Redisバージョン3.2.100を使用しています。
redis templateの代わりにRedis Cache Managerを使用し、redistemplateをcacheManagerに渡し、基本的にString Longのマップであるset expiresプロパティを使用して、キャッシュ名とセットを追加できます。有効期限、つまり存続時間(TTL)。
CacheManagerのsetDefaultExpirationメソッドを使用して、すべてのキャッシュに同じ有効期限を設定できます。
@SuppressWarnings({ "rawtypes", "unused" })
@Configuration
@EnableCaching(proxyTargetClass = true, mode = AdviceMode.ASPECTJ, order = 1)
@PropertySource("classpath:/application.properties")
public class CacheConfigImpl extends CachingConfigurerSupport {
private @Value("${redis.ip}") String redisHost;
private @Value("${redis.port}") int redisPort;
private static final Map<String, Long> cacheMap = new HashMap<String, Long>();
static {
cacheMap.put("method1cache", 600L);
cacheMap.put("method2cache", 600L);
cacheMap.put("method3cache", 800L);
}
@Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
@Bean
public JedisConnectionFactory redisConnectionFactory() {
JedisConnectionFactory redisConnectionFactory = new JedisConnectionFactory();
redisConnectionFactory.setHostName(CustomPropertyLoader.getProperty("redis.ip"));
redisConnectionFactory.setPort(Integer.parseInt(CustomPropertyLoader.getProperty("redis.port")));
return redisConnectionFactory;
}
@Bean
public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, String> redisTemplate = new RedisTemplate<String, String>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
@Bean(name = "RCacheManager")
public CacheManager cacheManager(RedisTemplate redisTemplate) {
RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate);
cacheManager.setExpires(cacheMap);
cacheManager.setUsePrefix(true);
final String redis_client_name = CustomPropertyLoader.getProperty("redis.client.name");
cacheManager.setCachePrefix(new RedisCachePrefix() {
private final RedisSerializer<String> serializer = new StringRedisSerializer();
private final String delimiter = ":";
public byte[] prefix(String cacheName) {
return this.serializer
.serialize(redis_client_name.concat(this.delimiter).concat(cacheName).concat(this.delimiter));
}
});
return cacheManager;
}
}
実際には、[Redisson] Redis Java RMapCache
オブジェクトを使用するクライアント。マップごとにttl
およびmaxIdle
を設定する機能を提供します。エントリ。例:
// implements Java.util.concurrent.ConcurrentMap interface
RMapCache<String, SomeObject> map = redisson.getMapCache("anyMap");
// ttl = 10 minutes,
map.put("key1", new SomeObject(), 10, TimeUnit.MINUTES);
// ttl = 10 minutes, maxIdleTime = 10 seconds
map.put("key1", new SomeObject(), 10, TimeUnit.MINUTES, 10, TimeUnit.SECONDS);
実際には、Redisハッシュ内の個々のキーに対してTTLを期限切れにすることも設定することもできません。期限切れにするか、TTLハッシュのみを設定できます。これをサポートしたい場合は、データ構造を変更する必要があります。
これが不可能な理由のリンクです。以下は Redis expire からの抜粋です。
私の知る限り、redisは機能よりもパフォーマンスを重視しています。 redisのメモリ効率の良いハッシュ実装の目的を無効にします。ハッシュキー値フィールドは常にフル機能のredisオブジェクトとして表されるわけではないため(ハッシュが小さいためメモリを節約するために線形配列として格納できます)、ハッシュキーフィールドにTTLを含めることはできません。
また、このリンク ハッシュフィールドに有効期限を設定できるようにする は、有効期限を処理するためにデータ構造を変更するのに役立つ場合があります
キーにTTLを設定するには、cacheManagerの複数のBeanを作成し、個々のBeanにTTLを設定します。使用方法に応じて、必要なcachemanagerを使用できます。これが私が実装したものです。
@Configuration("cacheConfig")
@EnableCaching
public class CacheConfig extends CachingConfigurerSupport{
@Bean
public JedisConnectionFactory redisConnectionFactory() {
System.out.println("redisConnectionFactory");
JedisConnectionFactory redisConnectionFactory = new JedisConnectionFactory();
// Defaults
redisConnectionFactory.setHostName("127.0.0.1");
redisConnectionFactory.setPort(6379);
return redisConnectionFactory;
}
@Bean
public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory cf) {
System.out.println("redisTemplate");
RedisTemplate<String, String> redisTemplate = new RedisTemplate<String, String>();
redisTemplate.setConnectionFactory(cf);
redisTemplate.setKeySerializer(new StringRedisSerializer());
return redisTemplate;
}
@Bean
@Primary
public CacheManager cacheManager2(RedisTemplate redisTemplate) {
RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate);
// Number of seconds before expiration. Defaults to unlimited (0)
cacheManager.setDefaultExpiration(20);
cacheManager.setUsePrefix(true);
return cacheManager;
}
@Bean
public CacheManager cacheManager1(RedisTemplate redisTemplate) {
RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate);
// Number of seconds before expiration. Defaults to unlimited (0)
cacheManager.setDefaultExpiration(60);
cacheManager.setUsePrefix(true);
return cacheManager;
}
}
上記で作成したcachemanager Beanを使用するには、
@Cacheable(value = "users", key = "#userId.toString()", cacheManager ="cacheManager2")
@RequestMapping(value = "/{userId}", method = RequestMethod.GET)
public User getUser(@PathVariable String userId) {
LOG.info("Getting user with ID {}.: "+userId);
return userService.fetchUserDataonUsers(userId);
}
@Cacheable(value = "users", key = "#userId.toString()", cacheManager ="cacheManager1")
@RequestMapping(value = "data/{userId}", method = RequestMethod.GET)
public String getUserData(@PathVariable String userId) {
LOG.info("Getting user with ID getUserData {}.: "+userId);
return userService.fetchUserDataonUsers(userId).toString();
}
cacheManager ="cacheManager2"
で@Cacheable
を定義すると、構成で定義されたcacheManager2
に対してTTL setが使用されます。cacheManager1
についても同様です。
この目的でQuartzを採用できます(Redisレコードのttlの実装)。 Spring Bootを使用する場合、自動構成 Scheduler が自動的に行われます。したがって、サービス層に直接自動配線できます。
@Autowired
private Scheduler scheduler;
次に、次のようなジョブを実装する必要があります(この例では、Spring Redis Dataを使用しています)。
@Slf4j
@Component
public class RemoveExpiredRecordJob implements Job {
@Autowired
public RedisRepository redisRepository;
@Override
public void execute(JobExecutionContext jobExecutionContext) {
String key = jobExecutionContext
.getJobDetail()
.getKey()
.getName();
redisRepository.deleteById(key);
log.info("Record removed due timeout :: {}", key);
}
}
次に、JobDetailおよびTriggerを作成するためのロジックをカプセル化できます
@Component
public class SchedulerComponentBuilder {
public JobDetail getJobDetail (String key, Class<? extends org.quartz.Job> clazz) {
return JobBuilder.newJob().ofType(clazz)
.storeDurably(false)
.withIdentity(key)
.withDescription("This key will be removed from Redis store when time expires.")
.build();
}
public Trigger getTrigger(int ttl, JobDetail jobDetail) {
Java.util.Calendar calendar = Java.util.Calendar.getInstance();
calendar.add(Java.util.Calendar.SECOND, ttl);
return TriggerBuilder.newTrigger().forJob(jobDetail)
.withDescription("This trigger fires once to remove an expired record from Redis store.")
.startAt(calendar.getTime())
.build();
}
}
最後に、Redisリポジトリにレコードを保存した直後に、このレコード(uniqueKey)を削除するジョブを次のようにスケジュールする必要があります。
@Autowired
private SchedulerComponentBuilder schedulerComponentBuilder;
private void schedule(String uniqueKey, int ttl) {
try {
JobDetail jobDetail = schedulerComponentBuilder.getJobDetail(uniqueKey, RemoveExpiredRecordJob.class);
Trigger jobTrigger = schedulerComponentBuilder.getTrigger(ttl, jobDetail);
scheduler.scheduleJob(jobDetail,jobTrigger);
log.info("Job is scheduled :: {}", jobDetail);
} catch (SchedulerException e) {
log.error("Filed to schedule a job {}", e);
throw new RuntimeException(e);
}
}
Spring Data Redisを使用しています。300秒後にエンティティを期限切れにするために@Redishash(timeToLive=300)
アノテーションを使用しています。
これは私のpom.xml
からの抜粋です
...
...
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.7.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
...
...
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
...
...
私のRedisConfig.class
@Configuration
@Log4j2
@EnableRedisRepositories(basePackageClasses = ConsentOTP.class)
public class RedisConfig {
@Value("${spring.redis.Host}")
private String Host;
@Value("${spring.redis.port}")
private Integer port;
@Value("${spring.redis.password}")
private String password;
@Bean
JedisConnectionFactory jedisConnectionFactory() {
log.info("=================================================================");
log.info("redis config : {} : {} ", Host, port);
log.info("=================================================================");
RedisStandaloneConfiguration config = new RedisStandaloneConfiguration(Host, port);
config.setPassword(RedisPassword.of(password));
return new JedisConnectionFactory(config);
}
}
そして、エンティティクラスConsentOtp.class
@RedisHash(value = "ConsentOTP", timeToLive = 300)
@Data
@NoArgsConstructor
public class ConsentOTP implements Serializable {
private static final long serialVersionUID = 1708925807375596799L;
private String id;
private LocalDateTime timestamp;
private String otp;
public ConsentOTP(String personId, LocalDateTime timestamp, String otp) {
this.id = personId;
this.timestamp = timestamp;
this.otp = otp;
}
}
これが私のRedisリポジトリです
public interface ConsentOtpRepository extends CrudRepository<ConsentOTP, String> {
}