一、配置
1.1 引入maven
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.17.0</version>
</dependency>
1.2 配置文件
spring.redis.database=0
spring.redis.password=
spring.redis.timeout=3000
#sentinel/cluster/single
spring.redis.mode=single
#连接池配置
spring.redis.pool.max-idle=16
spring.redis.pool.min-idle=8
spring.redis.pool.max-active=8
spring.redis.pool.max-wait=3000
spring.redis.pool.conn-timeout=3000
spring.redis.pool.so-timeout=3000
spring.redis.pool.size=10
#单机配置
spring.redis.single.address=192.168.60.23:6379
#集群配置
spring.redis.cluster.scan-interval=1000
spring.redis.cluster.nodes=
spring.redis.cluster.read-mode=SLAVE
spring.redis.cluster.retry-attempts=3
spring.redis.cluster.failed-attempts=3
spring.redis.cluster.slave-connection-pool-size=64
spring.redis.cluster.master-connection-pool-size=64
spring.redis.cluster.retry-interval=1500
#哨兵配置
spring.redis.sentinel.master=business-master
spring.redis.sentinel.nodes=
spring.redis.sentinel.master-onlyWrite=true
spring.redis.sentinel.fail-max=3
1.3 配置文件读取
/**
* @author: huangyibo
* @Date: 2022/6/23 10:56
* @Description: 配置文件读取
*/
@ConfigurationProperties(prefix="spring.redis", ignoreUnknownFields = false)
@Data
@ToString
public class RedisProperties {
private int database;
/**
* 等待节点回复命令的时间。该时间从命令发送成功时开始计时
*/
private int timeout;
private String password;
private String mode;
/**
* 池配置
*/
private RedisPoolProperties pool;
/**
* 单机信息配置
*/
private RedisSingleProperties single;
/**
* 集群 信息配置
*/
private RedisClusterProperties cluster;
/**
* 哨兵配置
*
*/
private RedisSentinelProperties sentinel;
}
/**
* @author: huangyibo
* @Date: 2022/6/23 11:00
* @Description: redis 池配置
*/
@Data
@ToString
public class RedisPoolProperties {
private int maxIdle;
private int minIdle;
private int maxActive;
private int maxWait;
private int connTimeout;
private int soTimeout;
/**
* 池大小
*/
private int size;
}
/**
* @author: huangyibo
* @Date: 2022/6/23 11:02
* @Description: 单节点配置
*/
@Data
@ToString
public class RedisSingleProperties {
private String address;
}
/**
* @author: huangyibo
* @Date: 2022/6/23 11:03
* @Description: 集群配置
*/
@Data
@ToString
public class RedisClusterProperties {
/**
* 集群状态扫描间隔时间,单位是毫秒
*/
private int scanInterval;
/**
* 集群节点
*/
private String nodes;
/**
* 默认值: SLAVE(只在从服务节点里读取)设置读取操作选择节点的模式。 可用值为: SLAVE - 只在从服务节点里读取。
* MASTER - 只在主服务节点里读取。 MASTER_SLAVE - 在主从服务节点里都可以读取
*/
private String readMode;
/**
* (从节点连接池大小) 默认值:64
*/
private int slaveConnectionPoolSize;
/**
* 主节点连接池大小)默认值:64
*/
private int masterConnectionPoolSize;
/**
* (命令失败重试次数) 默认值:3
*/
private int retryAttempts;
/**
*命令重试发送时间间隔,单位:毫秒 默认值:1500
*/
private int retryInterval;
/**
* 执行失败最大次数默认值:3
*/
private int failedAttempts;
}
/**
* @author: huangyibo
* @Date: 2022/6/23 11:06
* @Description: 哨兵配置
*/
@Data
@ToString
public class RedisSentinelProperties {
/**
* 哨兵master 名称
*/
private String master;
/**
* 哨兵节点
*/
private String nodes;
/**
* 哨兵配置
*/
private boolean masterOnlyWrite;
/**
*
*/
private int failMax;
}
1.4 CacheConfiguration
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.StringUtils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* @author: huangyibo
* @Date: 2022/6/23 11:08
* @Description:
*/
@Configuration
@EnableConfigurationProperties(RedisProperties.class)
public class CacheConfiguration {
@Autowired
private RedisProperties redisProperties;
@Configuration
@ConditionalOnClass({Redisson.class})
@ConditionalOnExpression("'${spring.redis.mode}'=='single' or '${spring.redis.mode}'=='cluster' or '${spring.redis.mode}'=='sentinel'")
protected class RedissonSingleClientConfiguration {
/**
* 单机模式 redisson 客户端
*/
@Bean
@ConditionalOnProperty(name="spring.redis.mode", havingValue="single")
public RedissonClient redissonSingle() {
Config config = new Config();
String node = redisProperties.getSingle().getAddress();
node = node.startsWith("redis://") node : "redis://" + node;
SingleServerConfig serverConfig = config.useSingleServer()
.setAddress(node)
.setTimeout(redisProperties.getPool().getConnTimeout())
.setConnectionPoolSize(redisProperties.getPool().getSize())
.setConnectionMinimumIdleSize(redisProperties.getPool().getMinIdle());
if(!StringUtils.isEmpty(redisProperties.getPassword())) {
serverConfig.setPassword(redisProperties.getPassword());
}
return Redisson.create(config);
}
/**
* 集群模式的 redisson 客户端
*
* @return
*/
@Bean
@ConditionalOnProperty(name = "spring.redis.mode", havingValue = "cluster")
public RedissonClient redissonCluster() {
System.out.println("cluster redisProperties:" + redisProperties.getCluster());
Config config = new Config();
String[] nodes = redisProperties.getCluster().getNodes().split(",");
List<String> newNodes = new ArrayList<>(nodes.length);
Arrays.stream(nodes).forEach((index) -> newNodes.add(
index.startsWith("redis://") index : "redis://" + index)
);
ClusterServersConfig serverConfig = config.useClusterServers()
.addNodeAddress(newNodes.toArray(new String[0])
).setScanInterval(
redisProperties.getCluster().getScanInterval()
).setIdleConnectionTimeout(
redisProperties.getPool().getSoTimeout()
).setConnectTimeout(
redisProperties.getPool().getConnTimeout()
).setRetryAttempts(
redisProperties.getCluster().getRetryAttempts()
).setRetryInterval(
redisProperties.getCluster().getRetryInterval()
).setMasterConnectionPoolSize(
redisProperties.getCluster().getMasterConnectionPoolSize()
).setSlaveConnectionPoolSize(
redisProperties.getCluster().getSlaveConnectionPoolSize()
).setTimeout(
redisProperties.getTimeout()
);
if (!StringUtils.isEmpty(redisProperties.getPassword())) {
serverConfig.setPassword(redisProperties.getPassword());
}
return Redisson.create(config);
}
/**
* 哨兵模式 redisson 客户端
* @return
*/
@Bean
@ConditionalOnProperty(name = "spring.redis.mode", havingValue = "sentinel")
public RedissonClient redissonSentinel() {
System.out.println("sentinel redisProperties:" + redisProperties.getSentinel());
Config config = new Config();
String[] nodes = redisProperties.getSentinel().getNodes().split(",");
List<String> newNodes = new ArrayList<>(nodes.length);
Arrays.stream(nodes).forEach((index) -> newNodes.add(
index.startsWith("redis://") index : "redis://" + index)
);
SentinelServersConfig serverConfig = config.useSentinelServers()
.addSentinelAddress(newNodes.toArray(new String[0]))
.setMasterName(redisProperties.getSentinel().getMaster())
.setReadMode(ReadMode.SLAVE)
.setTimeout(redisProperties.getTimeout())
.setMasterConnectionPoolSize(redisProperties.getPool().getSize())
.setSlaveConnectionPoolSize(redisProperties.getPool().getSize());
if (!StringUtils.isEmpty(redisProperties.getPassword())) {
serverConfig.setPassword(redisProperties.getPassword());
}
return Redisson.create(config);
}
}
}
二、Redisson工具类
import org.redisson.api.*;
import org.redisson.client.codec.StringCodec;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;
/**
* @author: huangyibo
* @Date: 2022/6/23 15:01
* @Description:
*/
@Component
public class RedisUtils {
/**
* 默认缓存时间
*/
private static final Long DEFAULT_EXPIRED = 32000L;
/**
* 自动装配redisson client对象
*/
@Resource
private RedissonClient redissonClient;
/**
* 用于操作key
* @return RKeys 对象
*/
public RKeys getKeys() {
return redissonClient.getKeys();
}
/**
* 移除缓存
*
* @param key
*/
public void delete(String key) {
redissonClient.getBucket(key).delete();
}
/**
* 获取getBuckets 对象
*
* @return RBuckets 对象
*/
public RBuckets getBuckets() {
return redissonClient.getBuckets();
}
/**
* 读取缓存中的字符串,永久有效
*
* @param key 缓存key
* @return 字符串
*/
public String getStr(String key) {
RBucket<String> bucket = redissonClient.getBucket(key);
return bucket.get();
}
/**
* 缓存字符串
*
* @param key
* @param value
*/
public void setStr(String key, String value) {
RBucket<String> bucket = redissonClient.getBucket(key);
bucket.set(value);
}
/**
* 缓存带过期时间的字符串
*
* @param key 缓存key
* @param value 缓存值
* @param expired 缓存过期时间,long类型,必须传值
*/
public void setStr(String key, String value, long expired) {
RBucket<String> bucket = redissonClient.getBucket(key, StringCodec.INSTANCE);
bucket.set(value, expired <= 0L DEFAULT_EXPIRED : expired, TimeUnit.SECONDS);
}
/**
* string 操作,如果不存在则写入缓存(string方式,不带有redisson的格式信息)
*
* @param key 缓存key
* @param value 缓存值
* @param expired 缓存过期时间
*/
public Boolean setIfAbsent(String key, String value, long expired) {
RBucket<String> bucket = redissonClient.getBucket(key, StringCodec.INSTANCE);
return bucket.trySet(value, expired <= 0L DEFAULT_EXPIRED : expired, TimeUnit.SECONDS);
}
/**
* 如果不存在则写入缓存(string方式,不带有redisson的格式信息),永久保存
*
* @param key 缓存key
* @param value 缓存值
*/
public Boolean setIfAbsent(String key, String value) {
RBucket<String> bucket = redissonClient.getBucket(key, StringCodec.INSTANCE);
return bucket.trySet(value);
}
/**
* 判断缓存是否存在
*
* @param key
* @return true 存在
*/
public Boolean isExists(String key) {
return redissonClient.getBucket(key).isExists();
}
/**
* 获取RList对象
*
* @param key RList的key
* @return RList对象
*/
public <T> RList<T> getList(String key) {
return redissonClient.getList(key);
}
/**
* 获取RMapCache对象
*
* @param key
* @return RMapCache对象
*/
public <K, V> RMapCache<K, V> getMap(String key) {
return redissonClient.getMapCache(key);
}
/**
* 获取RSET对象
*
* @param key
* @return RSET对象
*/
public <T> RSet<T> getSet(String key) {
return redissonClient.getSet(key);
}
/**
* 获取RScoredSortedSet对象
*
* @param key
* @param <T>
* @return RScoredSortedSet对象
*/
public <T> RScoredSortedSet<T> getScoredSortedSet(String key) {
return redissonClient.getScoredSortedSet(key);
}
}
三、常用RKeys的API操作
每个Redisson对象实例都会有一个与之对应的Redis数据实例,可以通过调用getName方法来取得redis数据实例的名称(key),所有于Redis key相关的操作都归纳在RKeys这个接口里。
RKeys keys = client.getKeys();
//获取所有key值
Iterable<String> allKeys = keys.getKeys();
//模糊查询所有包含关键字key的值
Iterable<String> foundedKeys = keys.getKeysByPattern("key");
//删除多个key值
long numOfDeletedKeys = keys.delete("obj1", "obj2", "obj3");
//模糊删除key值
long deletedKeysAmount = keys.deleteByPattern("test?");
//随机获取key
String randomKey = keys.randomKey();
//查询当前有多少个key
long keysAmount = keys.count();
具体demo
private void getKeys() {
RKeys keys = redisUtils.getRedisKeys();
Iterable<String> allKeys = keys.getKeys();
StringBuilder sb = new StringBuilder();
for (String key : allKeys) {
sb = sb.append(key).append(",");
}
log.info("所有的key:{}", sb.substring(0, sb.length() - 1));
// 模糊查询以 map 打头的所有 key
allKeys = keys.getKeysByPattern("map*");
sb = new StringBuilder();
for (String key : allKeys) {
sb = sb.append(key).append(",");
}
log.info("模糊匹配到的key:{}", sb.substring(0, sb.length() - 1));
}
其中,getKeysByPattern是基于redis的scan命令实现。
四、通用对象桶Object Bucket
Redisson的分布式RBucket Java对象是一种通用对象桶,可以用来存放任意类型的对象。除了同步接口外,还提供异步(Async)、反射式(Reactive)和RxJava2标准的接口。还可以通过RBuckets接口实现批量操作多个RBucket对象。
/**
* String 数据类型
*/
private void strDemo() {
redisUtils.setStr(DEMO_STR, "Hello, String.");
log.info("String 测试数据:{}", redisUtils.getStr(DEMO_STR));
redisUtils.setStr("myBucket", "myBucketIsXxx");
RBuckets buckets = redisUtils.getBuckets();
Map<String, String> foundBuckets = buckets.get("myBucket*");
Map<String, Object> map = new HashMap<>();
map.put("myBucket1", "value1");
map.put("myBucket2", 30L);
// 同时保存全部通用对象桶。
buckets.set(map);
Map<String, String> loadedBuckets = buckets.get("myBucket1", "myBucket2", "myBucket3");
log.info("跨桶String 测试数据:{}", loadedBuckets);
map.put("myBucket3", 320L);
}
五、散列 Hash
基于Redisson的分布式映射结构的RMap Java对象实现了java.util.concurrent.ConcurrentMap和java.util.Map接口,与HashMap不同的是,RMap 保持了元素的插入顺序。该对象的最大容量受Redis限制,最大元素数量是4294967295个。
/**
* Hash类型
*/
private void hashDemo() {
RMap<Object, Object> map = redisUtils.getMap("mapDemo");
map.put("demoId1", "123");
map.put("demoId100", "13000");
Object demoId1Obj = map.get("demoId1");
log.info("Hash 测试数据:{}", demoId1Obj);
}
六、集合 Set
基于Redisson的分布式Set结构的RSet Java对象实现了java.util.Set接口,通过元素的互相状态比较保证了每个元素的唯一性,该对象的最大容量受Redis限制,最大元素数量是4294967295个。
/**
* Set 测试
*/
private void setDemo() {
RSet<String> set = redisUtils.getSet("setKey");
set.add("value777");
log.info("Set 测试数据");
Iterator<String> iterator = set.iterator();
while (iterator.hasNext()) {
String next = iterator.next();
log.info(next);
}
}
七、列表 List
基于Redisson的分布式列表 List 结构的RList Java对象在实现了java.util.List接口的同时,确保了元素插入时的顺序,该对象的最大容量受Redis限制,最大元素数量是4294967295个。
/**
* List数据类型
*/
private void listDemo() {
RList<String> list = redisUtils.getList("listDemo");
list.add("listValue1");
list.add("listValue2");
log.info("List 测试数据:{}", list.get(1));
}
综合示例
将上述demo放入一个API中,快速测试:
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.*;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
@Slf4j
@RestController
@RequestMapping(value = "/redisson", method = RequestMethod.POST)
public class StudyRedissonController {
@Resource
private RedisUtils redisUtils;
private static String DEMO_STR = "demoStr";
@PostMapping("/learnRedisson")
public void learnRedisson() {
//三种数据结构使用示例
strDemo();
hashDemo();
listDemo();
setDemo();
getKeys();
}
private void getKeys() {
RKeys keys = redisUtils.getKeys();
Iterable<String> allKeys = keys.getKeys();
StringBuilder sb = new StringBuilder();
for (String key : allKeys) {
sb = sb.append(key).append(",");
}
log.info("所有的key:{}", sb.substring(0, sb.length() - 1));
// 模糊查询以 map 打头的所有 key
allKeys = keys.getKeysByPattern("map*");
sb = new StringBuilder();
for (String key : allKeys) {
sb = sb.append(key).append(",");
}
log.info("模糊匹配到的key:{}", sb.substring(0, sb.length() - 1));
}
/**
* Hash类型
*/
private void hashDemo() {
RMap<Object, Object> map = redisUtils.getMap("mapDemo");
map.put("demoId1", "123");
map.put("demoId100", "13000");
Object demoId1Obj = map.get("demoId1");
log.info("Hash 测试数据:{}", demoId1Obj);
}
/**
* String 数据类型
*/
private void strDemo() {
redisUtils.setStr(DEMO_STR, "Hello, String.");
log.info("String 测试数据:{}", redisUtils.getStr(DEMO_STR));
redisUtils.setStr("myBucket", "myBucketIsXxx");
RBuckets buckets = redisUtils.getBuckets();
Map<String, String> foundBuckets = buckets.get("myBucket*");
Map<String, Object> map = new HashMap<>();
map.put("myBucket1", "value1");
map.put("myBucket2", 30L);
// 同时保存全部通用对象桶。
buckets.set(map);
Map<String, String> loadedBuckets = buckets.get("myBucket1", "myBucket2", "myBucket3");
log.info("跨桶String 测试数据:{}", loadedBuckets);
map.put("myBucket3", 320L);
}
/**
* List数据类型
*/
private void listDemo() {
RList<String> list = redisUtils.getList("listDemo");
list.add("listValue1");
list.add("listValue2");
log.info("List 测试数据:{}", list.get(1));
}
/**
* Set 测试
*/
private void setDemo() {
RSet<String> set = redisUtils.getSet("setKey");
set.add("value777");
log.info("Set 测试数据");
Iterator<String> iterator = set.iterator();
while (iterator.hasNext()) {
String next = iterator.next();
log.info(next);
}
}
}
参考:
https://blog.51cto.com/u_14028890/2308518
https://www.cnblogs.com/east7/p/16255253.html
https://www.cnblogs.com/east7/p/16255305.html