Redis【有与无】【Lettuce】L10.编解码器
本文章基于Redis 6.0.9版本,Lettuce 6.0.1.RELEASE版本
目录
1.编解码器
编解码器是一种可插入的机制,用于在应用程序和Redis之间对键和值进行代码转换。默认编解码器支持UTF-8编码的字符串键和值。
每个连接都可以将其编解码器传递给扩展的RedisClient.connect
方法:
StatefulRedisConnection<K, V> connect(RedisCodec<K, V> codec)
StatefulRedisPubSubConnection<K, V> connectPubSub(RedisCodec<K, V> codec)
Lettuce带有预定义的编解码器:
-
io.lettuce.core.codec.ByteArrayCodec
- 使用byte[]
作为键和值 -
io.lettuce.core.codec.StringCodec
- 使用字符串作为键和值。 使用默认字符集或指定的Charset
,并改进了对US_ASCII
和UTF-8
的支持。 -
io.lettuce.core.codec.CipherCodec
- 用于值的透明加密。 -
io.lettuce.core.codec.CompressionCodec
- 将GZIP
或DEFLATE
压缩应用于值。
Publish/Subscribe连接使用通道名称和键模式; 消息被视为值。
键和值可以彼此独立地编码,这意味着键可以是java.lang.String
,而值是byte[]
。 许多其他星座也是可能的,例如:
- 如果你的数据映射到特定的Java类型,则将数据表示为JSON。 由于编解码器适用于所有操作,因此很难映射不同的类型。
-
使用Java序列化器(
ObjectInputStream
/ObjectOutputStream
)序列化数据。 允许类型安全的转换,但与其他语言的互操作性较低 - 使用Kryo序列化数据,以改善类型安全的序列化。
- 任何专门的编解码器,例如
BitStringCodec
(请参见下文)
1.1.为什么用ByteBuffer
而不是byte[]
RedisCodec
接口接受并返回ByteBuffer
以进行数据交换。 ByteBuffer
不考虑基础字节的来源。 Lettuce 3.x的byte[]
接口要求用户为数组提供准确的数据以进行交换。 因此,如果你有一个只想使用一个子集的数组,则需要创建一个字节数组的新实例并复制数据。 如果你使用其他字节源(例如netty的ByteBuffer
或NIO ByteBuffer
),则同样适用。 用于解码的ByteBuffer
是指向基础数据的指针。 用于编码数据的ByteBuffer
可以是纯指针或已分配的内存。 生菜不会释放任何内存(例如池缓冲)。
1.2.编解码器中的多样性
就像在技术的其他每个领域一样,在编解码器方面也没有一个万能的解决方案。 Redis数据结构提供了多种方式。编解码器的关键和价值限制是有意的,在便利性和简单性之间取得了平衡。 Redis API在编码和解码特定数据元素时允许更多差异。 一个很好的例子是Redis哈希。 哈希由其键标识,但存储另一个key/value对。 可以使用与哈希键不同的方法来对键值对的键进行编码。 另一种不同的方法可能是在列表和集合之间使用不同的编码。 使用基本编解码器(例如UTF-8或字节数组)并在基本编解码器之上执行自己的转换通常是更好的主意。
1.3.多线程
编解码器中的一个关键点是编解码器是共享资源,可以被多个线程使用。 你的编解码器需要是线程安全的(无共享,共享或同步)。 每个逻辑Lettuce连接都使用其编解码器实例。 一旦有多个线程发出命令或使用Redis Cluster,便会共享编解码器实例。
1.4.压缩
在Redis中存储更大的数据块时,压缩可能是一个好主意。 任何文本数据结构(例如JSON或XML)都适合压缩。 压缩是在编解码器级别处理的,这意味着你不必更改应用程序即可应用压缩。 CompressionCodec
使用GZIP或Deflate压缩为值提供基本和透明的压缩:
例子1.压缩编解码器的用法
StatefulRedisConnection<String, Object> connection = client.connect(
CompressionCodec.valueCompressor(new SerializedObjectCodec(), CompressionCodec.CompressionType.GZIP)).sync();
StatefulRedisConnection<String, String> connection = client.connect(
CompressionCodec.valueCompressor(StringCodec.UTF8, CompressionCodec.CompressionType.DEFLATE)).sync();
压缩可以与任何编解码器一起使用,压缩器仅包装内部RedisCodec并压缩/解压缩(compresses/decompresses)交换的数据。 你可以使用提供自己的编解码器的相同方式来构建自己的压缩器。
1.5.例子
例子2. BitString编解码器
public class BitStringCodec extends StringCodec {
@Override
public String decodeValue(ByteBuffer bytes) {
StringBuilder bits = new StringBuilder(bytes.remaining() * 8);
while (bytes.remaining() > 0) {
byte b = bytes.get();
for (int i = 0; i < 8; i++) {
bits.append(Integer.valueOf(b >>> i & 1));
}
}
return bits.toString();
}
}
StatefulRedisConnection<String, String> connection = client.connect(new BitStringCodec());
RedisCommands<String, String> redis = connection.sync();
redis.setbit(key, 0, 1);
redis.setbit(key, 1, 1);
redis.setbit(key, 2, 0);
redis.setbit(key, 3, 0);
redis.setbit(key, 4, 0);
redis.setbit(key, 5, 1);
redis.get(key) == "00100011"
例子3. JDK序列化器
public class SerializedObjectCodec implements RedisCodec<String, Object> {
private Charset charset = Charset.forName("UTF-8");
@Override
public String decodeKey(ByteBuffer bytes) {
return charset.decode(bytes).toString();
}
@Override
public Object decodeValue(ByteBuffer bytes) {
try {
byte[] array = new byte[bytes.remaining()];
bytes.get(array);
ObjectInputStream is = new ObjectInputStream(new ByteArrayInputStream(array));
return is.readObject();
} catch (Exception e) {
return null;
}
}
@Override
public ByteBuffer encodeKey(String key) {
return charset.encode(key);
}
@Override
public ByteBuffer encodeValue(Object value) {
try {
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
ObjectOutputStream os = new ObjectOutputStream(bytes);
os.writeObject(value);
return ByteBuffer.wrap(bytes.toByteArray());
} catch (IOException e) {
return null;
}
}
}