基于Redission高级应用10-RMap原理及工具类封装
愉快的五一过去了,迎来的是连续六天的工作时间,学习也将继续。
概述:
RMap
是 Redisson 提供的一个 Java 对象,它实现了 java.util.concurrent.ConcurrentMap
接口,是对 Redis 的哈希数据结构的封装。Redisson 是一个在 Redis 的基础上提供了许多分布式数据结构和服务的 Java 客户端库。RMap
通过使用 Redis 哈希表提供了一个分布式的 Map
实现。原理:
RMap
内部使用 Redis 的哈希(hash)数据类型来存储键值对。Redis 哈希是一个键值对集合,是存储对象属性的理想选择。RMap
提供的操作,如 put
、get
、remove
等,都是通过 Redis 命令来实现的。
当对 RMap
执行操作时,Redisson 会将这些操作转换为 Redis 的命令,然后通过网络发送到 Redis 服务���执行。例如,当调用 RMap.put(key, value)
方法时,Redisson 会发送一个 HSET
命令到 Redis 服务器。
优点:
- 分布式特性:
RMap
由于底层依赖于 Redis,因此天然具有分布式特性,可以在多个应用实例间共享数据。 - 并发支持:
RMap
实现了java.util.concurrent.ConcurrentMap
接口,提供了线程安全的操作。 - 高性能: Redis 作为内存数据库,提供了非常高的读写速度,
RMap
因此也继承了这一特性。 - 数据持久化: Redis 支持数据持久化到磁盘,因此使用
RMap
存储的数据可以在系统重启后恢复。 - 特性丰富:
RMap
支持许多高级特性,如监听器、过期键、事务操作等。
缺点:
- 内存限制: 由于 Redis 是内存数据库,
RMap
存储的数据量受限于服务器内存大小。 - 网络延迟: 所有操作都需要通过网络与 Redis 服务器通信,网络延迟可能会影响性能。
- 成本: 对于需要大量内存来存储数据的应用,使用 Redis 可能会增加成本。
- 数据一致性: 如果在多个节点上部署 Redisson 客户端,且 Redis 配置为主从复制模式,可能会遇到数据一致性问题,因为 Redis 的复制是异步的。
- 复杂性: 对于简单应用来说,引入 Redisson 和 Redis 可能会增加系统的复杂性。
RMap
是 Redisson 中的一个接口,它提供了一个分布式且可扩展的映射(Map)。
流程图:
以下步骤:
- 一个客户端 (
Client
) 使用put(k, v)
方法向RMap
添加一个键值对。 RMap
返回一个确认操作的消息 (Acknowledgement for put operation
)。- 客户端使用
get(k)
方法从RMap
获取与键k
关联的值。 RMap
返回与键k
关联的值 (Value associated with key k
)。- 客户端使用
remove(k)
方法从RMap
中删除一个键值对。 RMap
返回一个确认删除操作的消息 (Acknowledgement for remove operation
)。- 客户端使用
containsKey(k)
方法检查RMap
是否包含键k
。 RMap
返回一个布尔结果 (Boolean result for containsKey
) 表示是否包含该键。
每个操作都是由客户端发起,并且 RMap
对象对每个操作都给出了相应的响应。这个流程图可以帮助理解 RMap
提供的基本操作和客户端与之交互的方式。
时序图:
在这个时序图中:
Client
向RMap
发送put(key, value)
消息以添加一个键值对。RMap
返回操作的确认(Acknowledgment)。Client
向RMap
发送get(key)
消息以检索与键关联的值。RMap
返回与键关联的值。Client
向RMap
发送remove(key)
消息以移除一个键值对。RMap
返回操作的确认。Client
向RMap
发送containsKey(key)
消息以检查是否含有特定的键。RMap
返回一个布尔值表示是否包含该键。
这个时序图显示了 RMap
操作的基本流程,展示了客户端与 RMap
之间的交互。在实际应用中,每个操作可能还会涉及到更多的细节,例如错误处理和异步操作的处理。
工具类:
封装 RMap
的基本功能:
import org.redisson.api.RMap;
import org.redisson.api.RMapCache;
import org.redisson.api.RedissonClient;
import org.redisson.client.codec.Codec;
import org.redisson.codec.JsonJacksonCodec;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
/**
* @Author derek_smart
* @Date 202/4/28 10:21
* @Description RMap 工具类
* <p>
*/
@Component
public class RedissonMapHelper<K, V> {
private static final Logger logger = LoggerFactory.getLogger(RedissonMapHelper.class);
private final RedissonClient redissonClient;
private final Codec codec;
@Autowired
public RedissonMapHelper(RedissonClient redissonClient, Codec codec) {
this.redissonClient = redissonClient;
// Default to JSON codec, can be customized or set via configuration
this.codec = codec != null ? codec : new JsonJacksonCodec();
}
public <K, V> V get(String mapName, K key) {
try {
RMap<K, V> map = redissonClient.getMap(mapName);
return map.get(key);
} catch (Exception e) {
logger.error("Error getting value from map: {}", mapName, e);
return null;
}
}
public <K, V> V getForCodec(String mapName, K key) {
try {
RMap<K, V> map = redissonClient.getMap(mapName, codec);
return map.get(key);
} catch (Exception e) {
logger.error("Error getting value from map: {}", mapName, e);
return null;
}
}
// ... (Other methods with exception handling and logging)
public <K, V> V put(String mapName, K key, V value) {
try {
RMap<K, V> map = redissonClient.getMap(mapName);
return map.put(key, value);
} catch (Exception e) {
logger.error("Error putting value into map: {}", mapName, e);
return null;
}
}
public V putIfAbsent(String mapName, K key, V value) {
RMap<K, V> map = redissonClient.getMap(mapName);
return map.putIfAbsent(key, value);
}
public boolean remove(String mapName, K key) {
RMap<K, V> map = redissonClient.getMap(mapName);
return map.remove(key) != null;
}
public boolean removeForCodec(String mapName, K key) {
try {
RMapCache<K, V> mapCache = redissonClient.getMapCache(mapName, codec);
return mapCache.fastRemove(key) > 0;
} catch (Exception e) {
logger.error("Error removing key from map: {}", mapName, e);
// Implement a retry mechanism
return false;
}
}
public boolean containsKey(String mapName, K key) {
RMap<K, V> map = redissonClient.getMap(mapName);
return map.containsKey(key);
}
public long size(String mapName) {
RMap<K, V> map = redissonClient.getMap(mapName);
return map.size();
}
public void clear(String mapName) {
RMap<K, V> map = redissonClient.getMap(mapName);
map.clear();
}
public void delete(String mapName) {
RMap<K, V> map = redissonClient.getMap(mapName);
map.delete();
}
/* public V putWithTTL(String mapName, K key, V value, long ttl, TimeUnit timeUnit) {
RMap<K, V> map = redissonClient.getMap(mapName);
V previousValue = map.put(key, value);
map.expireKey(key, ttl, timeUnit);
return previousValue;
}*/
public V putWithTTL(String mapName, K key, V value, long ttl, TimeUnit timeUnit) {
RMapCache<K, V> mapCache = redissonClient.getMapCache(mapName);
V previousValue = mapCache.put(key, value, ttl, timeUnit);
return previousValue;
}
public <K, V> CompletableFuture<Void> putAllAsync(String mapName, Map<? extends K, ? extends V> map) {
return (CompletableFuture<Void>) redissonClient.getMap(mapName).putAllAsync(map);
}
public <K, V> CompletableFuture<Map<K, V>> getAllAsync(String mapName, Iterable<? extends K> keys) {
return (CompletableFuture<Map<K, V>>) redissonClient.getMap(mapName).getAllAsync((Set<Object>) keys);
}
public <K, V> void putAll(String mapName, Map<? extends K, ? extends V> map) {
try {
RMap<K, V> rMap = redissonClient.getMap(mapName, codec);
rMap.putAll(map);
} catch (Exception e) {
logger.error("Error putting all values into map: {}", mapName, e);
}
}
public <K, V> Map<K, V> getAll(String mapName, Iterable<? extends K> keys) {
try {
RMap<K, V> rMap = redissonClient.getMap(mapName, codec);
return rMap.getAll((Set<K>) keys);
} catch (Exception e) {
logger.error("Error getting values from map: {}", mapName, e);
return null;
}
}
}
工具类总结:
在这个工具类中,定义了一些基本的方法,如 get
、put
、putIfAbsent
、remove
等,这些方法都是通过 Redisson 客户端与 Redis 服务器通信的。还添加了一个 putWithTTL
方法,它允许为特定的键设置一个生存时间(TTL),这样键就会在经过指定的时间后自动从 Redis 中删除。
引入了异步方法,如 putAllAsync
和 getAllAsync
,它们返回 CompletableFuture
对象。这些方法允许非阻塞地执行操作,从而提高性能。
请注意,这个工具类是泛型的,这意味着可以用它来存储和检索任何类型的键和值,只要它们是可序列化的。
工具类功能点:
- 异常处理:为了提高鲁棒性,应该捕获可能发生的异常,并根据需要进行适当的处理。
- 泛型方法:使用泛型方法而不是在类级别定义泛型,这样可以为不同类型的键和值使用同一个实例。
- 配置化:允许从配置文件中读取 Redis 地址和其他参数,而不是硬编码。
- 序列化:确保提供了对键和值序列化策略的支持。
- 资源管理:确保 Redis 连接被适当管理,例如使用连接池等。
- 日志记录:添加日志记录以便于调试和监控。
- 批量操作:提供批量操作的方法,以减少网络往返次数。
- 异步操作:提供异步操作的方法,以提高性能。
- 增加批量操作方法:这些方法可以在单次调用中处理多个键和值,减少网络往返次数,从而提高性能。
- 定制序列化策略:Redisson 允许定制序列化和反序列化策略。可以根据需要选择不同的序列化器,例如 JSON、Avro、Kryo 等。
- 支持异步和同步操作:提供异步(non-blocking)和同步(blocking)版本的方法,以便用户根据具体场景选择。
- 优化资源使用:确保 Redis 连接和其他资源被正确管理,例如使用 Redisson 的连接池功能。
- 增加配置和灵活性:允许通过配置来设置默认的 map 名称、序列化策略等,使得工具类更加灵活。
- 增加安全性:考虑到安全性,对敏感数据进行加密。
Redission配置
Spring 配置中,需要配置 RedissonClient
bean,这样它就可以被自动装配到工具类中。这里是一个简单的配置示例:
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class RedissonConfig {
@Bean
public RedissonClient redissonClient() {
Config config = new Config();
config.useSingleServer()
.setAddress("redis://127.0.0.1:6379");
return Redisson.create(config);
}
}
在这个配置类中,创建了一个 RedissonClient
的 bean,它将连接到本地运行的 Redis 服务器。在实际部署时,需要根据的环境配置 Redis 服务器的地址和其他设置。
现在,可以在应用程序中注入 RedissonMapHelper
并使用它来与 Redis 中的 RMap
进行交互。
总的来说,RMap
是一个功能强大的分布式 Map
实现,适用于需要高性能、高并发和分布式特性的场景。选择使用 RMap
时,需要根据应用的具体需求权衡其优缺点。
转载自:https://juejin.cn/post/7364971296163758114