一奈辰、配置
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
#單機(jī)配置
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;
/**
* 等待節(jié)點(diǎn)回復(fù)命令的時(shí)間。該時(shí)間從命令發(fā)送成功時(shí)開始計(jì)時(shí)
*/
private int timeout;
private String password;
private String mode;
/**
* 池配置
*/
private RedisPoolProperties pool;
/**
* 單機(jī)信息配置
*/
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: 單節(jié)點(diǎn)配置
*/
@Data
@ToString
public class RedisSingleProperties {
private String address;
}
/**
* @author: huangyibo
* @Date: 2022/6/23 11:03
* @Description: 集群配置
*/
@Data
@ToString
public class RedisClusterProperties {
/**
* 集群狀態(tài)掃描間隔時(shí)間钱豁,單位是毫秒
*/
private int scanInterval;
/**
* 集群節(jié)點(diǎn)
*/
private String nodes;
/**
* 默認(rèn)值: SLAVE(只在從服務(wù)節(jié)點(diǎn)里讀让蘧)設(shè)置讀取操作選擇節(jié)點(diǎn)的模式。 可用值為: SLAVE - 只在從服務(wù)節(jié)點(diǎn)里讀取挺益。
* MASTER - 只在主服務(wù)節(jié)點(diǎn)里讀取歉糜。 MASTER_SLAVE - 在主從服務(wù)節(jié)點(diǎn)里都可以讀取
*/
private String readMode;
/**
* (從節(jié)點(diǎn)連接池大小) 默認(rèn)值:64
*/
private int slaveConnectionPoolSize;
/**
* 主節(jié)點(diǎn)連接池大型凇)默認(rèn)值:64
*/
private int masterConnectionPoolSize;
/**
* (命令失敗重試次數(shù)) 默認(rèn)值:3
*/
private int retryAttempts;
/**
*命令重試發(fā)送時(shí)間間隔现恼,單位:毫秒 默認(rèn)值:1500
*/
private int retryInterval;
/**
* 執(zhí)行失敗最大次數(shù)默認(rèn)值:3
*/
private int failedAttempts;
}
/**
* @author: huangyibo
* @Date: 2022/6/23 11:06
* @Description: 哨兵配置
*/
@Data
@ToString
public class RedisSentinelProperties {
/**
* 哨兵master 名稱
*/
private String master;
/**
* 哨兵節(jié)點(diǎn)
*/
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 {
/**
* 單機(jī)模式 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 {
/**
* 默認(rèn)緩存時(shí)間
*/
private static final Long DEFAULT_EXPIRED = 32000L;
/**
* 自動(dòng)裝配redisson client對(duì)象
*/
@Resource
private RedissonClient redissonClient;
/**
* 用于操作key
* @return RKeys 對(duì)象
*/
public RKeys getKeys() {
return redissonClient.getKeys();
}
/**
* 移除緩存
*
* @param key
*/
public void delete(String key) {
redissonClient.getBucket(key).delete();
}
/**
* 獲取getBuckets 對(duì)象
*
* @return RBuckets 對(duì)象
*/
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);
}
/**
* 緩存帶過期時(shí)間的字符串
*
* @param key 緩存key
* @param value 緩存值
* @param expired 緩存過期時(shí)間叉袍,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 緩存過期時(shí)間
*/
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對(duì)象
*
* @param key RList的key
* @return RList對(duì)象
*/
public <T> RList<T> getList(String key) {
return redissonClient.getList(key);
}
/**
* 獲取RMapCache對(duì)象
*
* @param key
* @return RMapCache對(duì)象
*/
public <K, V> RMapCache<K, V> getMap(String key) {
return redissonClient.getMapCache(key);
}
/**
* 獲取RSET對(duì)象
*
* @param key
* @return RSET對(duì)象
*/
public <T> RSet<T> getSet(String key) {
return redissonClient.getSet(key);
}
/**
* 獲取RScoredSortedSet對(duì)象
*
* @param key
* @param <T>
* @return RScoredSortedSet對(duì)象
*/
public <T> RScoredSortedSet<T> getScoredSortedSet(String key) {
return redissonClient.getScoredSortedSet(key);
}
}
三润文、常用RKeys的API操作
每個(gè)Redisson對(duì)象實(shí)例都會(huì)有一個(gè)與之對(duì)應(yīng)的Redis數(shù)據(jù)實(shí)例,可以通過調(diào)用getName方法來取得redis數(shù)據(jù)實(shí)例的名稱(key)殿怜,所有于Redis key相關(guān)的操作都?xì)w納在RKeys這個(gè)接口里典蝌。
RKeys keys = client.getKeys();
//獲取所有key值
Iterable<String> allKeys = keys.getKeys();
//模糊查詢所有包含關(guān)鍵字key的值
Iterable<String> foundedKeys = keys.getKeysByPattern("key");
//刪除多個(gè)key值
long numOfDeletedKeys = keys.delete("obj1", "obj2", "obj3");
//模糊刪除key值
long deletedKeysAmount = keys.deleteByPattern("test?");
//隨機(jī)獲取key
String randomKey = keys.randomKey();
//查詢當(dāng)前有多少個(gè)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命令實(shí)現(xiàn)头谜。
四骏掀、通用對(duì)象桶Object Bucket
Redisson的分布式RBucket Java對(duì)象是一種通用對(duì)象桶,可以用來存放任意類型的對(duì)象柱告。除了同步接口外截驮,還提供異步(Async)、反射式(Reactive)和RxJava2標(biāo)準(zhǔn)的接口际度。還可以通過RBuckets接口實(shí)現(xiàn)批量操作多個(gè)RBucket對(duì)象葵袭。
/**
* String 數(shù)據(jù)類型
*/
private void strDemo() {
redisUtils.setStr(DEMO_STR, "Hello, String.");
log.info("String 測(cè)試數(shù)據(jù):{}", 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);
// 同時(shí)保存全部通用對(duì)象桶。
buckets.set(map);
Map<String, String> loadedBuckets = buckets.get("myBucket1", "myBucket2", "myBucket3");
log.info("跨桶String 測(cè)試數(shù)據(jù):{}", loadedBuckets);
map.put("myBucket3", 320L);
}
五乖菱、散列 Hash
基于Redisson的分布式映射結(jié)構(gòu)的RMap Java對(duì)象實(shí)現(xiàn)了java.util.concurrent.ConcurrentMap和java.util.Map接口坡锡,與HashMap不同的是蓬网,RMap 保持了元素的插入順序。該對(duì)象的最大容量受Redis限制鹉勒,最大元素?cái)?shù)量是4294967295個(gè)帆锋。
/**
* 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 測(cè)試數(shù)據(jù):{}", demoId1Obj);
}
六、集合 Set
基于Redisson的分布式Set結(jié)構(gòu)的RSet Java對(duì)象實(shí)現(xiàn)了java.util.Set接口贸弥,通過元素的互相狀態(tài)比較保證了每個(gè)元素的唯一性窟坐,該對(duì)象的最大容量受Redis限制,最大元素?cái)?shù)量是4294967295個(gè)绵疲。
/**
* Set 測(cè)試
*/
private void setDemo() {
RSet<String> set = redisUtils.getSet("setKey");
set.add("value777");
log.info("Set 測(cè)試數(shù)據(jù)");
Iterator<String> iterator = set.iterator();
while (iterator.hasNext()) {
String next = iterator.next();
log.info(next);
}
}
七哲鸳、列表 List
基于Redisson的分布式列表 List 結(jié)構(gòu)的RList Java對(duì)象在實(shí)現(xiàn)了java.util.List接口的同時(shí),確保了元素插入時(shí)的順序盔憨,該對(duì)象的最大容量受Redis限制徙菠,最大元素?cái)?shù)量是4294967295個(gè)。
/**
* List數(shù)據(jù)類型
*/
private void listDemo() {
RList<String> list = redisUtils.getList("listDemo");
list.add("listValue1");
list.add("listValue2");
log.info("List 測(cè)試數(shù)據(jù):{}", list.get(1));
}
綜合示例
將上述demo放入一個(gè)API中郁岩,快速測(cè)試:
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() {
//三種數(shù)據(jù)結(jié)構(gòu)使用示例
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 測(cè)試數(shù)據(jù):{}", demoId1Obj);
}
/**
* String 數(shù)據(jù)類型
*/
private void strDemo() {
redisUtils.setStr(DEMO_STR, "Hello, String.");
log.info("String 測(cè)試數(shù)據(jù):{}", 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);
// 同時(shí)保存全部通用對(duì)象桶婿奔。
buckets.set(map);
Map<String, String> loadedBuckets = buckets.get("myBucket1", "myBucket2", "myBucket3");
log.info("跨桶String 測(cè)試數(shù)據(jù):{}", loadedBuckets);
map.put("myBucket3", 320L);
}
/**
* List數(shù)據(jù)類型
*/
private void listDemo() {
RList<String> list = redisUtils.getList("listDemo");
list.add("listValue1");
list.add("listValue2");
log.info("List 測(cè)試數(shù)據(jù):{}", list.get(1));
}
/**
* Set 測(cè)試
*/
private void setDemo() {
RSet<String> set = redisUtils.getSet("setKey");
set.add("value777");
log.info("Set 測(cè)試數(shù)據(jù)");
Iterator<String> iterator = set.iterator();
while (iterator.hasNext()) {
String next = iterator.next();
log.info(next);
}
}
}
參考:
https://blog.51cto.com/u_14028890/2308518