Redisson 使用
概述
Redisson是一个在Redis的基础上实现的Java驻内存数据网格(In-Memory Data Grid)。它不仅提供了一系列的分布式的Java常用对象,还提供了许多分布式服务。其中包括(BitSet
, Set
, Multimap
, SortedSet
, Map
, List
, Queue
, BlockingQueue
, Deque
, BlockingDeque
, Semaphore
, Lock
, AtomicLong
, CountDownLatch
, Publish / Subscribe
, Bloom filter
, Remote service
, Spring cache
, Executor service
, Live Object service
, Scheduler service
) Redisson提供了使用Redis的最简单和最便捷的方法。Redisson的宗旨是促进使用者对Redis的关注分离(Separation of Concern),从而让使用者能够将精力更集中地放在处理业务逻辑上。
配置方法(单节点)(Spring boot)
引入依赖
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>3.13.1</version>
</dependency>
配置文件(application.properties)
spring.redis.database=0
spring.redis.host=localhost
spring.redis.port=6379
spring.redis.redisson.config=classpath:redisson.yaml
编写 redisson.yaml
对应配置java类(singleServerConfig.class)
各个配置项说明参考
---
singleServerConfig:
idleConnectionTimeout: 10000
# pingTimeout: 1000
connectTimeout: 10000
timeout: 3000
retryAttempts: 3
retryInterval: 1500
# reconnectionTimeout: 3000
# failedAttempts: 3
password: null
subscriptionsPerConnection: 5
clientName: null
address: "redis://127.0.0.1:6379"
subscriptionConnectionMinimumIdleSize: 1
subscriptionConnectionPoolSize: 50
connectionMinimumIdleSize: 32
connectionPoolSize: 64
database: 0
# dnsMonitoring: false
# dnsMonitoringInterval: 5000
threads: 0
nettyThreads: 0
codec: !<org.redisson.codec.JsonJacksonCodec> {}
transportMode: NIO
其它配置方法
https://github.com/redisson/redisson/wiki/2.-%E9%85%8D%E7%BD%AE%E6%96%B9%E6%B3%95
使用
@Autowired
StringRedisTemplate redisTemplate;
@Autowired
RedisTemplate redisTemplate;
@Autowired
RedissonClient redissonClient;
数据序列化
默认序列化方式
org.redisson.codec.JsonJacksonCodec
自定义序列化
继承 BaseCodec抽象类 然后配置codec 参数即可 可自定义编码器和解码器
package com.wxy.redissondemo.config;
import com.fasterxml.jackson.annotation.*;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder;
import com.fasterxml.jackson.databind.type.TypeFactory;
import com.wxy.redissondemo.utils.ObjectUtlis;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import io.netty.buffer.ByteBufInputStream;
import io.netty.buffer.ByteBufOutputStream;
import org.redisson.client.codec.BaseCodec;
import org.redisson.client.handler.State;
import org.redisson.client.protocol.Decoder;
import org.redisson.client.protocol.Encoder;
import org.redisson.codec.JsonJacksonCodec;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.datatype.XMLGregorianCalendar;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.GeneralSecurityException;
import java.util.Base64;
/**
* @Description:
*/
public class EncryptJsonJacksonCodec extends BaseCodec {
public static final EncryptJsonJacksonCodec INSTANCE = new EncryptJsonJacksonCodec();
private static final String KEY = "1234567890abcdef";
@JsonIdentityInfo(generator= ObjectIdGenerators.IntSequenceGenerator.class, property="@id")
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY,
getterVisibility = JsonAutoDetect.Visibility.PUBLIC_ONLY,
setterVisibility = JsonAutoDetect.Visibility.NONE,
isGetterVisibility = JsonAutoDetect.Visibility.NONE)
public static class ThrowableMixIn {
}
protected final ObjectMapper mapObjectMapper;
private final Encoder encoder = new Encoder() {
@Override
public ByteBuf encode(Object in) throws IOException {
ByteBuf out = ByteBufAllocator.DEFAULT.buffer();
try {
ByteBufOutputStream os = new ByteBufOutputStream(out);
byte[] bytes = ObjectUtlis.toByteArray(in);
bytes = encrypt(KEY.getBytes(),bytes);
mapObjectMapper.writeValue((OutputStream) os, Base64.getEncoder().encodeToString(bytes));
return os.buffer();
} catch (IOException e) {
out.release();
throw e;
} catch (Exception e) {
out.release();
throw new IOException(e);
}
}
};
private final Decoder<Object> decoder = new Decoder<Object>() {
@Override
public Object decode(ByteBuf buf, State state) throws IOException {
Object object = mapObjectMapper.readValue((InputStream) new ByteBufInputStream(buf), Object.class);
try {
byte[]byi = decrypt(KEY.getBytes(),Base64.getDecoder().decode(object.toString().getBytes()));
return ObjectUtlis.toObject(byi);
} catch (GeneralSecurityException e) {
e.printStackTrace();
}
return object;
}
};
public EncryptJsonJacksonCodec() {
this(new ObjectMapper());
}
public EncryptJsonJacksonCodec(ClassLoader classLoader) {
this(createObjectMapper(classLoader, new ObjectMapper()));
}
public EncryptJsonJacksonCodec(ClassLoader classLoader, EncryptJsonJacksonCodec codec) {
this(createObjectMapper(classLoader, codec.mapObjectMapper.copy()));
}
protected static ObjectMapper createObjectMapper(ClassLoader classLoader, ObjectMapper om) {
TypeFactory tf = TypeFactory.defaultInstance().withClassLoader(classLoader);
om.setTypeFactory(tf);
return om;
}
public EncryptJsonJacksonCodec(ObjectMapper mapObjectMapper) {
this.mapObjectMapper = mapObjectMapper.copy();
init(this.mapObjectMapper);
initTypeInclusion(this.mapObjectMapper);
}
// 加密:
public static byte[] encrypt(byte[] key, byte[] input) throws GeneralSecurityException {
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
SecretKey keySpec = new SecretKeySpec(key, "AES");
cipher.init(Cipher.ENCRYPT_MODE, keySpec);
return cipher.doFinal(input);
}
// 解密:
public static byte[] decrypt(byte[] key, byte[] input) throws GeneralSecurityException {
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
SecretKey keySpec = new SecretKeySpec(key, "AES");
cipher.init(Cipher.DECRYPT_MODE, keySpec);
return cipher.doFinal(input);
}
protected void initTypeInclusion(ObjectMapper mapObjectMapper) {
TypeResolverBuilder<?> mapTyper = new ObjectMapper.DefaultTypeResolverBuilder(ObjectMapper.DefaultTyping.NON_FINAL) {
public boolean useForType(JavaType t) {
switch (_appliesFor) {
case NON_CONCRETE_AND_ARRAYS:
while (t.isArrayType()) {
t = t.getContentType();
}
// fall through
case OBJECT_AND_NON_CONCRETE:
return (t.getRawClass() == Object.class) || !t.isConcrete();
case NON_FINAL:
while (t.isArrayType()) {
t = t.getContentType();
}
// to fix problem with wrong long to int conversion
if (t.getRawClass() == Long.class) {
return true;
}
if (t.getRawClass() == XMLGregorianCalendar.class) {
return false;
}
return !t.isFinal(); // includes Object.class
default:
// case JAVA_LANG_OBJECT:
return t.getRawClass() == Object.class;
}
}
};
mapTyper.init(JsonTypeInfo.Id.CLASS, null);
mapTyper.inclusion(JsonTypeInfo.As.PROPERTY);
mapObjectMapper.setDefaultTyping(mapTyper);
}
protected void init(ObjectMapper objectMapper) {
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
objectMapper.setVisibility(objectMapper.getSerializationConfig()
.getDefaultVisibilityChecker()
.withFieldVisibility(JsonAutoDetect.Visibility.ANY)
.withGetterVisibility(JsonAutoDetect.Visibility.NONE)
.withSetterVisibility(JsonAutoDetect.Visibility.NONE)
.withCreatorVisibility(JsonAutoDetect.Visibility.NONE));
objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
objectMapper.enable(JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN);
objectMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
objectMapper.enable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY);
objectMapper.addMixIn(Throwable.class, JsonJacksonCodec.ThrowableMixIn.class);
}
@Override
public Decoder<Object> getValueDecoder() {
return decoder;
}
@Override
public Encoder getValueEncoder() {
return encoder;
}
@Override
public ClassLoader getClassLoader() {
if (mapObjectMapper.getTypeFactory().getClassLoader() != null) {
return mapObjectMapper.getTypeFactory().getClassLoader();
}
return super.getClassLoader();
}
public ObjectMapper getObjectMapper() {
return mapObjectMapper;
}
}
分布式对象
具体使用方式请参考相关api
https://github.com/redisson/redisson/wiki/6.-%E5%88%86%E5%B8%83%E5%BC%8F%E5%AF%B9%E8%B1%A1
通用对象桶 RBucket
二进制流 RBinaryStream
地理空间对象桶RGeo
对redis版本有要求需要redis 版本大于3.0
BitSet
原子整长形 RAtomiclong
原子双精度浮点RAtomicDouble
布隆过滤器 Bloom Filter
基数估计算法 RHyperLogLog
整长型累加器RLongAdder
双精度浮点累加器RLongDouble
限流器RRateLimiter
分布式集合
基于Redis的Redisson的分布式映射结构的RMap
Java对象实现了java.util.concurrent.ConcurrentMap
接口和java.util.Map
接口。与HashMap不同的是,RMap保持了元素的插入顺序。该对象的最大容量受Redis限制,最大元素数量是4 294 967 295
个。
在特定的场景下,映射缓存(Map)上的高度频繁的读取操作,使网络通信都被视为瓶颈时,可以使用Redisson提供的带有本地缓存功能的映射。
RMap
使用样例
RMap<String, SomeObject> map = redisson.getMap("anyMap");
SomeObject prevObject = map.put("123", new SomeObject());
SomeObject currentObject = map.putIfAbsent("323", new SomeObject());
SomeObject obj = map.remove("123");
map.fastPut("321", new SomeObject());
map.fastRemove("321");
RFuture<SomeObject> putAsyncFuture = map.putAsync("321");
RFuture<Void> fastPutAsyncFuture = map.fastPutAsync("321");
map.fastPutAsync("321", new SomeObject());
map.fastRemoveAsync("321");
更多方法请参考redisson api
RMapCache
RMapCache<String, SomeObject> map = redisson.getMapCache("anyMap");
// 有效时间 ttl = 10分钟
map.put("key1", new SomeObject(), 10, TimeUnit.MINUTES);
// 有效时间 ttl = 10分钟, 最长闲置时间 maxIdleTime = 10秒钟
map.put("key1", new SomeObject(), 10, TimeUnit.MINUTES, 10, TimeUnit.SECONDS);
// 有效时间 = 3 秒钟
map.putIfAbsent("key2", new SomeObject(), 3, TimeUnit.SECONDS);
// 有效时间 ttl = 40秒钟, 最长闲置时间 maxIdleTime = 10秒钟
map.putIfAbsent("key2", new SomeObject(), 40, TimeUnit.SECONDS, 10, TimeUnit.SECONDS);
RLocalCachedMap
LocalCachedMapOptions options = LocalCachedMapOptions.defaults()
// 用于淘汰清除本地缓存内的元素
// 共有以下几种选择:
// LFU - 统计元素的使用频率,淘汰用得最少(最不常用)的。
// LRU - 按元素使用时间排序比较,淘汰最早(最久远)的。
// SOFT - 元素用Java的WeakReference来保存,缓存元素通过GC过程清除。
// WEAK - 元素用Java的SoftReference来保存, 缓存元素通过GC过程清除。
// NONE - 永不淘汰清除缓存元素。
.evictionPolicy(EvictionPolicy.NONE)
// 如果缓存容量值为0表示不限制本地缓存容量大小
.cacheSize(1000)
// 以下选项适用于断线原因造成了未收到本地缓存更新消息的情况。
// 断线重连的策略有以下几种:
// CLEAR - 如果断线一段时间以后则在重新建立连接以后清空本地缓存
// LOAD - 在服务端保存一份10分钟的作废日志
// 如果10分钟内重新建立连接,则按照作废日志内的记录清空本地缓存的元素
// 如果断线时间超过了这个时间,则将清空本地缓存中所有的内容
// NONE - 默认值。断线重连时不做处理。
.reconnectionStrategy(ReconnectionStrategy.NONE)
// 以下选项适用于不同本地缓存之间相互保持同步的情况
// 缓存同步策略有以下几种:
// INVALIDATE - 默认值。当本地缓存映射的某条元素发生变动时,同时驱逐所有相同本地缓存映射内的该元素
// UPDATE - 当本地缓存映射的某条元素发生变动时,同时更新所有相同本地缓存映射内的该元素
// NONE - 不做任何同步处理
.syncStrategy(SyncStrategy.INVALIDATE)
// 每个Map本地缓存里元素的有效时间,默认毫秒为单位
.timeToLive(10000)
// 或者
.timeToLive(10, TimeUnit.SECONDS)
// 每个Map本地缓存里元素的最长闲置时间,默认毫秒为单位
.maxIdle(10000)
// 或者
.maxIdle(10, TimeUnit.SECONDS);
RLocalCachedMap<String, Integer> map = redisson.getLocalCachedMap("test", options);
String prevObject = map.put("123", 1);
String currentObject = map.putIfAbsent("323", 2);
String obj = map.remove("123");
// 在不需要旧值的情况下可以使用fast为前缀的类似方法
map.fastPut("a", 1);
map.fastPutIfAbsent("d", 32);
map.fastRemove("b");
RFuture<String> putAsyncFuture = map.putAsync("321");
RFuture<Void> fastPutAsyncFuture = map.fastPutAsync("321");
map.fastPutAsync("321", new SomeObject());
map.fastRemoveAsync("321");
比较和区别
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LDKuKwQp-1597631162624)(C:\Users\Waspal\AppData\Roaming\Typora\typora-user-images\image-20200701143339436.png)]
数据淘汰策略
- 元素淘汰(Eviction) 类 – 带有元素淘汰(Eviction)机制的映射类允许针对一个映射中每个元素单独设定 有效时间 和 最长闲置时间 。
- 本地缓存(LocalCache) 类 – 本地缓存(Local Cache)也叫就近缓存(Near Cache)。这类映射的使用主要用于在特定的场景下,映射缓存(MapCache)上的高度频繁的读取操作,使网络通信都被视为瓶颈的情况。Redisson与Redis通信的同时,还将部分数据保存在本地内存里。这样的设计的好处是它能将读取速度提高最多 45倍 。 所有同名的本地缓存共用一个订阅发布话题,所有更新和过期消息都将通过该话题共享。
分布式锁
以下这些分布式锁都是继承自 java.util.concurrent.locks接口,因此使用方式基本差不多。如有不懂请参考
可重入锁
公平锁
读写锁
信号量
可过期性信号量
闭锁
Redis命令和Redisson对象匹配表
参考资料
https://www.javadoc.io/doc/org.redisson/redisson/3.10.0/index.html (redisson api文档)
https://github.com/redisson/redisson/wiki(使用样例文档)