一, 添加所需依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.5</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
二, redis配置参数
# redis
spring.redis.database=0
spring.redis.host=r-uf63gkx4qs0ygz3rag.redis.rds.aliyuncs.com
spring.redis.port=6379
spring.redis.password=ywjAYKDz28RFCJg9iRW4
# 连接超时时间
spring.redis.timeout=5000
# 连接池最大连接数(使用负值表示没有限制)
spring.redis.lettuce.pool.max-active=5000
# 连接池中的最小空闲连接
spring.redis.lettuce.pool.min-idle=1
# 连接池中的最大空闲连接
spring.redis.lettuce.pool.max-idle=100
# 连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.lettuce.pool.max-wait=1000
#在关闭客户端连接之前等待任务处理完成的最长时间,在这之后,无论任务是否执行完成,都会被执行器关闭,默认100ms
spring.redis.lettuce.shutdown-timeout=1000
#是否缓存空值
spring.cache.redis.cache-null-values=false
三, redis连接池设置
package com.shuinfo.ods_poc.config;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.data.redis.core.ReactiveRedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.time.Duration;
@Configuration
@EnableCaching
public class RedisConfig {
@Value("${spring.redis.database}")
private int database;
@Value("${spring.redis.host}")
private String host;
@Value("${spring.redis.password}")
private String password;
@Value("${spring.redis.port}")
private int port;
@Value("${spring.redis.timeout}")
private long timeout;
@Value("${spring.redis.lettuce.shutdown-timeout}")
private long shutDownTimeout;
@Value("${spring.redis.lettuce.pool.max-idle}")
private int maxIdle;
@Value("${spring.redis.lettuce.pool.min-idle}")
private int minIdle;
@Value("${spring.redis.lettuce.pool.max-active}")
private int maxActive;
@Value("${spring.redis.lettuce.pool.max-wait}")
private long maxWait;
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
@Bean
public LettuceConnectionFactory lettuceConnectionFactory() {
GenericObjectPoolConfig genericObjectPoolConfig = new GenericObjectPoolConfig();
genericObjectPoolConfig.setMaxIdle(maxIdle);
genericObjectPoolConfig.setMinIdle(minIdle);
genericObjectPoolConfig.setMaxTotal(maxActive);
genericObjectPoolConfig.setMaxWait(Duration.ofMillis(maxWait));
//每隔多少毫秒,空闲线程驱逐器关闭多余的空闲连接,且保持最少空闲连接可用,这个值最好设置大一点,否者影响性能
genericObjectPoolConfig.setTimeBetweenEvictionRuns(Duration.ofMillis(1000));
RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
redisStandaloneConfiguration.setDatabase(database);
redisStandaloneConfiguration.setHostName(host);
redisStandaloneConfiguration.setPort(port);
redisStandaloneConfiguration.setPassword(RedisPassword.of(password));
LettuceClientConfiguration clientConfig = LettucePoolingClientConfiguration.builder()
.commandTimeout(Duration.ofMillis(timeout))
.shutdownTimeout(Duration.ofMillis(shutDownTimeout))
.poolConfig(genericObjectPoolConfig)
.build();
LettuceConnectionFactory factory = new LettuceConnectionFactory(redisStandaloneConfiguration, clientConfig);
// factory.setShareNativeConnection(true);
// factory.setValidateConnection(false);
return factory;
}
@Bean
public RedisSerializationContext redisSerializationContext() {
RedisSerializationContext.RedisSerializationContextBuilder builder = RedisSerializationContext.newSerializationContext();
builder.key(StringRedisSerializer.UTF_8);
builder.value(RedisSerializer.json());
builder.hashKey(StringRedisSerializer.UTF_8);
builder.hashValue(StringRedisSerializer.UTF_8);
return builder.build();
}
@Bean
public ReactiveRedisTemplate<String, Object> reactiveRedisTemplate(LettuceConnectionFactory lettuceConnectionFactory) {
RedisSerializationContext serializationContext = redisSerializationContext();
return new ReactiveRedisTemplate(lettuceConnectionFactory,serializationContext);
}
}
四, 封装工具类
package com.shuinfo.ods_poc.utils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.ReactiveRedisTemplate;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.time.Duration;
@Component
@Slf4j
public class RedisUtils {
private static final Duration EXPIRED_DAY = Duration.ofDays(7);
private static final Duration EXPIRED_HOURS = Duration.ofHours(30);
private static final Duration EXPIRED_MINUTES = Duration.ofMinutes(1);
@Autowired
private ReactiveRedisTemplate reactiveRedisTemplate;
public <T> void put(String key,T value){
reactiveRedisTemplate.opsForValue()
.set(key,value,EXPIRED_HOURS).subscribe((b->{}));
}
public <T> Mono<T> get(String key){
return reactiveRedisTemplate.opsForValue()
.get(key);
}
public <T> void putList(String key, T value){
reactiveRedisTemplate.opsForSet().add(key,value).subscribe();
}
public <T> Flux<T> getList(String key){
return reactiveRedisTemplate.opsForSet().members(key);
}
public void remove(String key){
reactiveRedisTemplate.opsForSet().delete(key).subscribe();
}
public <T> void putZSet(String key, T value,String score){
reactiveRedisTemplate.opsForZSet().add(key,value,Double.valueOf(score))
.doOnError(e->{
log.error(e.toString());
}).subscribe();
}
public <T> Flux<T> getZSet(String key,Integer page,Integer size){
if(page == null || size == null){
return reactiveRedisTemplate.opsForZSet().rangeByScore(key,
org.springframework.data.domain.Range.unbounded());
}
return reactiveRedisTemplate.opsForZSet().rangeByScore(key,
org.springframework.data.domain.Range.unbounded(),
org.springframework.data.redis.connection
.RedisZSetCommands.Limit.limit()
.offset((page - 1) * size).count(size)
);
}
}
五, 使用
//存缓存
redisUtils.putList(RedisConstant.FIND_ORDER_BY_UID_AND_OTYPE
+ req.getMemberCode()+req.getBusinessTypeCode(),req);
redisUtils.putList(RedisConstant.FIND_ORDER_BY_SHOP_POS_INFO
+ req.getShopCode()+req.getOrderTime()+req.getPosRefId()
+ req.getReceiptNumber(),req);
六, 分布式锁(RedisTemplate)
package com.shuinfo.ods_poc.utils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.connection.RedisStringCommands;
import org.springframework.data.redis.connection.ReturnType;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.types.Expiration;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.nio.charset.Charset;
import java.util.concurrent.TimeUnit;
@Component
@Slf4j
public class RedisLockUtils {
@Resource
private RedisTemplate redisTemplate;
private static final Long DEFAULT_EXPIRE_TIME = 1L;
private static final TimeUnit DEFAULT_UNIT = TimeUnit.SECONDS;
private static final String UNLOCK_LUA;
private static final String CHARCODE = "UTF-8";
/**
* 释放锁脚本,原子操作
*/
static {
StringBuilder sb = new StringBuilder();
sb.append("if redis.call(\"get\",KEYS[1]) == ARGV[1] ");
sb.append("then ");
sb.append(" return redis.call(\"del\",KEYS[1]) ");
sb.append("else ");
sb.append(" return 0 ");
sb.append("end ");
UNLOCK_LUA = sb.toString();
}
/**
* 获取分布式锁,原子操作
* @param lockKey
* @param requestId 唯一ID, 可以使用UUID.randomUUID().toString();
* @param expire
* @param timeUnit
* @return
*/
public boolean tryLock(String lockKey, String requestId, long expire, TimeUnit timeUnit) {
try{
RedisCallback<Boolean> callback = (connection) ->
connection.set(lockKey.getBytes(Charset.forName(CHARCODE)),
requestId.getBytes(Charset.forName(CHARCODE)),
Expiration.seconds(timeUnit.toSeconds(expire)),
RedisStringCommands.SetOption.SET_IF_ABSENT);
return (Boolean)redisTemplate.execute(callback);
} catch (Exception e) {
log.error("redis lock error.", e);
}
return false;
}
public boolean tryLock(String lockKey, String requestId) {
return tryLock( lockKey, requestId,DEFAULT_EXPIRE_TIME,DEFAULT_UNIT);
}
/**
* 释放锁
* @param lockKey
* @param requestId 唯一ID
* @return
*/
public boolean releaseLock(String lockKey, String requestId) {
RedisCallback<Boolean> callback = (connection) ->
connection.eval(UNLOCK_LUA.getBytes(), ReturnType.BOOLEAN ,
1, lockKey.getBytes(Charset.forName(CHARCODE)),
requestId.getBytes(Charset.forName(CHARCODE)));
return (Boolean)redisTemplate.execute(callback);
}
/**
* 获取Redis锁的value值
* @param lockKey
* @return
*/
public String get(String lockKey) {
try {
RedisCallback<String> callback = (connection) ->
new String(connection.get(lockKey.getBytes()), Charset.forName(CHARCODE));
return (String)redisTemplate.execute(callback);
} catch (Exception e) {
log.error("get redis occurred an exception", e);
}
return null;
}
}
可参照阿里文档:
通过客户端程序连接Redis_云数据库 Redis 版(Redis)-阿里云帮助中心
注: redis的参数还需要关注redis服务端的参数,有些默认的参数可能实际并不适合