1、redis配置
import com.soul.data.lettuce.config.AbstractSpringDataRedisConfig;
import com.soul.data.redis.core.StringRedisTemplate;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.PropertySource;
import org.springframework.data.redis.connection.RedisPassword;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration;
import org.springframework.stereotype.Component;
import java.time.Duration;
@Component
@PropertySource("redis.properties")
public class RedisConfig extends AbstractSpringDataRedisConfig {
@Value("${user.version.redis.host}")
private String host;
@Value("${user.version.redis.port}")
private int port;
@Value("${user.version.redis.password}")
private String password;
@Bean(name = "userVersionRedisTemplate")
public StringRedisTemplate userVersionRedisTemplate() {
return redisTemplateChangedCommandTimeout(host, port, password);
}
protected LettuceConnectionFactory redisConnectionFactoryChangedCommandTimeout(String hostName, Integer port, String password) {
RedisStandaloneConfiguration redisConf = new RedisStandaloneConfiguration();
redisConf.setHostName(hostName);
redisConf.setPort(port);
redisConf.setPassword(RedisPassword.of(password));
//命令3秒超时(默认60秒)
LettuceClientConfiguration lettuceClientConfiguration = LettucePoolingClientConfiguration.builder()
.commandTimeout(Duration.ofSeconds(3))
.poolConfig(defaultPoolConfig())
.build();
LettuceConnectionFactory lettuceConnectionFactory = new LettuceConnectionFactory(redisConf, lettuceClientConfiguration);
lettuceConnectionFactory.afterPropertiesSet();
return lettuceConnectionFactory;
}
protected StringRedisTemplate redisTemplateChangedCommandTimeout(String hostName, Integer port, String password) {
StringRedisTemplate template = new StringRedisTemplate();
template.setConnectionFactory(this.redisConnectionFactoryChangedCommandTimeout(hostName, port, password));
template.afterPropertiesSet();
return template;
}
@Override
@Bean
public KeyGenerator keyGenerator() {
return defaultKeyGenerator();
}
}
2、scan redis的数据并设置过期时间
import com.soul.data.redis.core.RedisTemplate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.Objects;
@RestController
public class ExpiredController implements EnvironmentAware {
private static final Logger logger = LoggerFactory.getLogger(ExpiredController.class);
public static int EXPIRE_SECOND = 130 * 24 * 60 * 60;
@Resource(name = "userVersionRedisTemplate")
private RedisTemplate<String, String> userVersionRedisTemplate;
@RequestMapping("/expired")
public String expired(HttpServletRequest request, Integer count) {
count = Objects.isNull(count) ? 5000 : count;
String remoteHost = request.getRemoteHost();
if (!"127.0.0.1".equals(remoteHost)) {
return "0";
}
logger.info("expire data,count:{}", count);
String keyPattern = "u:v:*";
Integer result = doExpired(userVersionRedisTemplate, keyPattern, count);
logger.info("expire finished,keyPattern:{},count:{},result:{}", keyPattern, count, result);
return String.valueOf(result);
}
private Integer doExpired(RedisTemplate<String, String> redisTemplate, String keyPattern, Integer count) {
Integer total = redisTemplate.execute(new RedisCallback<Integer>() {
@Override
public Integer doInRedis(RedisConnection connection) throws DataAccessException {
Integer res = 0;
ScanOptions.ScanOptionsBuilder builder = new ScanOptions.ScanOptionsBuilder();
ScanOptions scanOptions = builder.match(keyPattern)
.count(count).build();
Cursor<byte[]> cursor = connection.scan(scanOptions);
long cursorId = cursor.getCursorId();
long position = cursor.getPosition();
logger.info("scan data,keyPattern:{},count:{},cursorId:{} position:{}", keyPattern, count, cursorId, position);
while (cursor.hasNext()) {
//紧急情况如redis支持不住,通过apollo改成true,退出循环
boolean interrupt = env.getProperty("interrupt", Boolean.class, Boolean.FALSE);
if (interrupt) {
logger.info("aborted program,keyPattern:{},count:{},cursorId:{} position:{},res:{}", keyPattern, count, cursorId, position, res);
return res;
}
byte[] bytes = cursor.next();
Long expireSecond = connection.ttl(bytes);
boolean result = false;
if (expireSecond == null || expireSecond.intValue() == -1) {
result = connection.expire(bytes, EXPIRE_SECOND);
res++;
if (res % 10000 == 0) {
try {
Thread.sleep(20);
logger.info("thread sleep 20ms");
} catch (InterruptedException e) {
logger.error("thread sleep error", e);
}
}
}
if (res != 0 && res % 1000 == 0) {
logger.info("expire result:{},key:{},res:{}", result, new String(bytes), res);
}
}
try {
cursor.close();
} catch (Exception e) {
logger.error("close cursor error", e);
}
return res;
}
});
return total;
}
private Environment env;
@Override
public void setEnvironment(Environment environment) {
this.env = environment;
}
}