原标题:Spring认证中国教育管理中心-Spring Data Redis框架教程三
10.15.支持类
Packageorg.springframework.data.redis.support提供了各种可重用的组件,这些组件依赖于 Redis 作为后备存储。目前,该包包含基于 Redis 的各种基于 JDK 的接口实现,例如原子计数器和 JDK集合。
原子计数器可以轻松包装 Redis 密钥增量,而集合可以轻松管理 Redis 密钥,同时将存储暴露或 API 泄漏降至最低。特别是,RedisSet和RedisZSet接口提供了对 Redis 支持的集合操作的轻松访问,例如intersection和union。RedisList实现List,Queue以及Deque合同(和它们的等效阻挡兄弟姐妹)上的Redis的顶部,露出存储作为FIFO(先入先出),LIFO(后进先出)或封端的集合以最小的配置. 以下示例显示了使用 的 bean 的配置RedisList:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="queue" class="org.springframework.data.redis.support.collections.DefaultRedisList">
<constructor-arg ref="redisTemplate"/>
<constructor-arg value="queue-key"/>
</bean>
</beans>
以下示例显示了一个 Java 配置示例Deque:
public class AnotherExample {
// injected
private Deque<String> queue;
public void addTag(String tag) {
queue.push(tag);
}
}
如前面的示例所示,消费代码与实际存储实现分离。事实上,没有任何迹象表明在下面使用了Redis。这使得从开发环境到生产环境的转变变得透明,并大大提高了可测试性(Redis 实现可以用内存中的实现代替)。
1.响应式Redis支持
本节介绍反应式 Redis 支持以及如何开始。响应式 Redis 支持自然与命令式 Redis 支持有一定的重叠。
11.1.Redis 要求
Spring Data Redis 目前与Lettuce集成,作为唯一的反应式 Java 连接器。Project Reactor用作反应式组合库。
11.2.使用响应式驱动程序连接到 Redis
使用 Redis 和 Spring 时的首要任务之一是通过 IoC 容器连接到存储。为此,需要一个 Java 连接器(或绑定)。无论你选择的库中,你必须使用
org.springframework.data.redis.connection包及其ReactiveRedisConnection和ReactiveRedisConnectionFactory接口的工作和取得积极connections对Redis的。
11.2.1.Redis 操作模式
Redis 可以作为独立服务器运行,使用Redis Sentinel或以Redis 集群模式运行。 Lettuce支持所有前面提到的连接类型。
11.2.2.ReactiveRedisConnection和ReactiveRedisConnectionFactory
ReactiveRedisConnection是 Redis 通信的核心,因为它处理与 Redis 后端的通信。它还自动将底层驱动程序异常转换为 Spring 一致的 DAO 异常层次结构,因此您可以在不更改任何代码的情况下切换连接器,因为操作语义保持不变。
ReactiveRedisConnectionFactory创建活动ReactiveRedisConnection实例。此外,工厂充当PersistenceExceptionTranslator实例,这意味着一旦声明,它们就可以让您进行透明的异常转换——例如,通过使用@Repository注释和 AOP 进行异常转换。有关更多信息,请参阅Spring Framework 文档中的专用部分。
根据底层配置,工厂可以返回新连接或现有连接(如果使用池或共享本机连接)。
使用 a 的最简单方法
ReactiveRedisConnectionFactory是通过 IoC 容器配置适当的连接器并将其注入 using 类。
11.2.3.配置生菜连接器
Spring Data Redis 通过
org.springframework.data.redis.connection.lettuce包支持Lettuce。
您可以
ReactiveRedisConnectionFactory按如下方式设置生菜:
@Bean
public ReactiveRedisConnectionFactory connectionFactory() {
return new LettuceConnectionFactory("localhost", 6379);
}
以下示例显示了一个更复杂的配置,包括 SSL 和超时,它使用
LettuceClientConfigurationBuilder:
@Bean
public ReactiveRedisConnectionFactory lettuceConnectionFactory() {
LettuceClientConfiguration clientConfig = LettuceClientConfiguration.builder()
.useSsl().and()
.commandTimeout(Duration.ofSeconds(2))
.shutdownTimeout(Duration.ZERO)
.build();
return new LettuceConnectionFactory(new RedisStandaloneConfiguration("localhost", 6379), clientConfig);
}
有关更详细的客户端配置调整,请参阅
LettuceClientConfiguration。
11.3.通过 ReactiveRedisTemplate 处理对象
大多数用户可能会使用ReactiveRedisTemplate及其对应的包,
org.springframework.data.redis.core. 由于其丰富的功能集,模板实际上是 Redis 模块的中心类。该模板为 Redis 交互提供了高级抽象。虽然ReactiveRedisConnection提供了接受和返回二进制值 ( ByteBuffer)的低级方法,但模板负责序列化和连接管理,使您无需处理此类细节。
此外,该模板提供操作视图(遵循 Redis 命令参考中的分组),提供丰富的通用接口,用于针对特定类型工作,如下表所述:
配置后,模板是线程安全的,可以跨多个实例重复使用。
ReactiveRedisTemplate大多数操作使用基于 Java 的序列化程序。这意味着模板写入或读取的任何对象都通过RedisElementWriter或 进行序列化或反序列化RedisElementReader。序列化上下文在构造时传递给模板,Redis 模块在
org.springframework.data.redis.serializer包中提供了几种可用的实现。有关更多信息,请参阅序列化程序。
以下示例显示了ReactiveRedisTemplate用于返回 a 的 a Mono:
@Configuration
class RedisConfiguration {
@Bean
ReactiveRedisTemplate<String, String> reactiveRedisTemplate(ReactiveRedisConnectionFactory factory) {
return new ReactiveRedisTemplate<>(factory, RedisSerializationContext.string());
}
}
public class Example {
@Autowired
private ReactiveRedisTemplate<String, String> template;
public Mono<Long> addLink(String userId, URL url) {
return template.opsForList().leftPush(userId, url.toExternalForm());
}
}
11.4.以字符串为中心的便利课程
因为它是存储在Redis的是一个键和值相当普遍java.lang.String,Redis的模块提供了一个基于字符串的扩展ReactiveRedisTemplate:
ReactiveStringRedisTemplate。是密集String作业的便捷一站式解决方案。除了绑定到String键之外,模板还使用 String-based RedisSerializationContext,这意味着存储的键和值是人类可读的(假设在 Redis 和您的代码中使用相同的编码)。以下示例显示ReactiveStringRedisTemplate在使用中:
@Configuration
class RedisConfiguration {
@Bean
ReactiveStringRedisTemplate reactiveRedisTemplate(ReactiveRedisConnectionFactory factory) {
return new ReactiveStringRedisTemplate<>(factory);
}
}
public class Example {
@Autowired
private ReactiveStringRedisTemplate redisTemplate;
public Mono<Long> addLink(String userId, URL url) {
return redisTemplate.opsForList().leftPush(userId, url.toExternalForm());
}
}
11.5.Redis 消息传递/发布订阅
Spring Data 为 Redis 提供了专门的消息传递集成,在功能和命名上与 Spring Framework 中的 JMS 集成非常相似;事实上,熟悉 Spring 中 JMS 支持的用户应该会有宾至如归的感觉。
Redis 消息传递大致可以分为两个功能区,即消息的生产或发布和消费或订阅,因此有快捷方式pubsub(发布/订阅)。所述ReactiveRedisTemplate类用于消息生成。对于异步接收,Spring Data 提供了一个专用的消息侦听器容器,用于消费消息流。仅出于订阅目的,ReactiveRedisTemplate提供了使用侦听器容器的精简替代方案。
包
org.springframework.data.redis.connection并org.springframework.data.redis.listener提供使用 Redis 消息传递的核心功能。
11.5.1.发送/发布消息
要发布消息,与其他操作一样,可以使用低级ReactiveRedisConnection或高级ReactiveRedisTemplate. 这两个实体都提供了一个发布方法,该方法接受需要发送的消息以及目标通道作为参数。虽然ReactiveRedisConnection需要原始数据,但ReactiveRedisTemplate允许将任意对象作为消息传入:
// send message through ReactiveRedisConnection
ByteBuffer msg = …
ByteBuffer channel = …
Mono<Long> publish = con.publish(msg, channel);
// send message through ReactiveRedisTemplate
ReactiveRedisTemplate template = …
Mono<Long> publish = template.convertAndSend("channel", "message");
11.5.2.接收/订阅消息
在接收端,可以通过直接命名或使用模式匹配来订阅一个或多个频道。后一种方法非常有用,因为它不仅允许使用一个命令创建多个订阅,而且还可以侦听订阅时尚未创建的频道(只要它们匹配模式)。
在底层,ReactiveRedisConnection提供subscribe和pSubscribe方法映射Redis命令以分别按模式按频道订阅。请注意,可以使用多个通道或模式作为参数。要更改订阅,只需查询 的频道和模式ReactiveSubscription。
Spring Data Redis 中的响应式订阅命令是非阻塞的,并且可能会在不发出元素的情况下结束。
如上所述,一旦订阅,连接就会开始等待消息。除了添加新订阅或修改/取消现有订阅之外,不能对其调用其他命令。subscribe、pSubscribe、unsubscribe、 或以外的命令pUnsubscribe是非法的,会导致异常。
为了接收消息,需要获取消息流。请注意,订阅仅发布在该特定订阅中注册的频道和模式的消息。消息流本身是一个热序列,它在不考虑需求的情况下生成元素。确保注册足够的需求以免耗尽消息缓冲区。
消息侦听器容器
Spring Data 提供
ReactiveRedisMessageListenerContainer它代表用户完成所有繁重的转换和订阅状态管理。
ReactiveRedisMessageListenerContainer充当消息侦听器容器。它用于从 Redis 通道接收消息并公开一个消息流,该消息流通过应用反序列化发出通道消息。负责注册接收消息、资源获取与释放、异常转换等。这允许您作为应用程序开发人员编写与接收消息(并对其作出反应)相关的(可能很复杂)业务逻辑,并将样板 Redis 基础设施问题委托给框架。消息流在发布者订阅时在 Redis 中注册订阅,如果订阅被取消则取消注册。
此外,为了最小化应用程序占用空间,
ReactiveRedisMessageListenerContainer允许多个侦听器共享一个连接和一个线程,即使它们不共享订阅。因此,无论应用程序跟踪多少个侦听器或通道,运行时成本在其整个生命周期内都将保持不变。此外,容器允许运行时配置更改,因此可以在应用程序运行时添加或删除侦听器,而无需重新启动。此外,容器使用惰性订阅方法,ReactiveRedisConnection仅在需要时使用 - 如果所有侦听器都取消订阅,则会自动执行清理。
消息侦听器容器本身不需要外部线程资源。它使用驱动程序线程来发布消息。
ReactiveRedisConnectionFactory factory = …
ReactiveRedisMessageListenerContainer container = new ReactiveRedisMessageListenerContainer(factory);
Flux<ChannelMessage<String, String>> stream = container.receive(ChannelTopic.of("my-channel"));
要等待并确保正确订阅,您可以使用receiveLater返回Mono<Flux>. 将得到的Mono完成具有内部出版商作为完成订阅给定主题的结果。通过拦截onNext信号,您可以同步服务器端订阅。
ReactiveRedisConnectionFactory factory = …
ReactiveRedisMessageListenerContainer container = new ReactiveRedisMessageListenerContainer(factory);
Mono<Flux<ChannelMessage<String, String>>> stream = container.receiveLater(ChannelTopic.of("my-channel"));
stream.doOnNext(inner -> // notification hook when Redis subscriptions are synchronized with the server)
.flatMapMany(Function.identity())
.…;
通过模板API订阅
如上所述,您可以直接使用ReactiveRedisTemplate订阅频道/模式。这种方法提供了一种直接但有限的解决方案,因为您无法在初始订阅之后添加订阅。尽管如此,您仍然可以通过返回的Flux使用例如控制消息流。take(Duration). 完成读取、出错或取消时,所有绑定资源将再次释放。
redisTemplate.listenToChannel("channel1", "channel2").doOnNext(msg -> {
// message processing ...
}).subscribe();
11.6.反应式脚本
您可以使用响应式基础架构运行 Redis 脚本,ReactiveScriptExecutor最好通过ReactiveRedisTemplate.
public class Example {
@Autowired
private ReactiveRedisTemplate<String, String> template;
public Flux<Long> theAnswerToLife() {
DefaultRedisScript<Long> script = new DefaultRedisScript<>();
script.setLocation(new ClassPathResource("META-INF/scripts/42.lua"));
script.setResultType(Long.class);
return reactiveTemplate.execute(script);
}
}
有关脚本命令的更多详细信息,请参阅脚本部分。
12.Redis集群
使用Redis 集群需要 Redis Server 3.0+ 版本。有关更多信息,请参阅集群教程。
12.1.启用Redis集群
集群支持基于与非集群通信相同的构建块。RedisClusterConnection是 的扩展RedisConnection,处理与 Redis 集群的通信并将错误转换为 Spring DAO 异常层次结构。 RedisClusterConnection实例是使用 来创建的RedisConnectionFactory,必须使用关联的 进行设置RedisClusterConfiguration,如以下示例所示:
示例 5. Redis 集群的示例 RedisConnectionFactory 配置
@Component
@ConfigurationProperties(prefix = "spring.redis.cluster")
public class ClusterConfigurationProperties {
/*
* spring.redis.cluster.nodes[0] = 127.0.0.1:7379
* spring.redis.cluster.nodes[1] = 127.0.0.1:7380
* ...
*/
List<String> nodes;
/**
* Get initial collection of known cluster nodes in format {@code host:port}.
*
* @return
*/
public List<String> getNodes() {
return nodes;
}
public void setNodes(List<String> nodes) {
this.nodes = nodes;
}
}
@Configuration
public class AppConfig {
/**
* Type safe representation of application.properties
*/
@Autowired ClusterConfigurationProperties clusterProperties;
public @Bean RedisConnectionFactory connectionFactory() {
return new JedisConnectionFactory(
new RedisClusterConfiguration(clusterProperties.getNodes()));
}
}
RedisClusterConfiguration也可以通过定义PropertySource并具有以下属性:
配置属性
spring.redis.cluster.nodes:逗号分隔的主机:端口对列表。
spring.redis.cluster.max-redirects:允许的集群重定向数。
初始配置将驱动程序库指向一组初始集群节点。实时集群重新配置导致的更改仅保留在本机驱动程序中,不会写回到配置中。
12.2.使用 Redis 集群连接
如前所述,Redis Cluster 的行为与单节点 Redis 甚至 Sentinel 监控的主副本环境不同。这是因为自动分片将密钥映射到 16384 个插槽之一,这些插槽分布在节点上。因此,涉及多个键的命令必须断言所有键映射到完全相同的槽以避免跨槽错误。单个集群节点仅提供一组专用密钥。针对一个特定服务器发出的命令仅返回该服务器提供的那些密钥的结果。作为一个简单的例子,考虑KEYS命令。当发送到集群环境中的服务器时,它只返回请求发送到的节点所服务的密钥,而不一定返回集群内的所有密钥。因此,要获取集群环境中的所有密钥,您必须从所有已知的主节点读取密钥。
虽然将特定键重定向到相应的插槽服务节点由驱动程序库处理,但更高级别的功能,例如跨节点收集信息或向集群中的所有节点发送命令,由RedisClusterConnection. 以前面的键示例为例,这意味着该keys(pattern)方法会获取集群中的每个主节点,并同时KEYS在每个主节点上运行命令,同时获取结果并返回累积的键集。仅请求单个节点的键RedisClusterConnection为这些方法提供了重载(例如,keys(node, pattern))。
ARedisClusterNode可以从
RedisClusterConnection.clusterGetNodes主机和端口或节点 Id 中获取或构建。
以下示例显示了在集群中运行的一组命令:
示例 6. 跨集群运行命令的示例
redis-cli@127.0.0.1:7379 > cluster nodes
6b38bb... 127.0.0.1:7379 master - 0 0 25 connected 0-5460
7bb78c... 127.0.0.1:7380 master - 0 1449730618304 2 connected 5461-10922
164888... 127.0.0.1:7381 master - 0 1449730618304 3 connected 10923-16383
b8b5ee... 127.0.0.1:7382 slave 6b38bb... 0 1449730618304 25 connected
RedisClusterConnection connection = connectionFactory.getClusterConnnection();
connection.set("thing1", value);
connection.set("thing2", value);
connection.keys("*");
connection.keys(NODE_7379, "*");
connection.keys(NODE_7380, "*");
connection.keys(NODE_7381, "*");
connection.keys(NODE_7382, "*");
主节点服务插槽 0 到 5460 复制到副本 7382
主节点服务时隙 5461 到 10922
主节点服务时隙 10923 到 16383
副本节点在 7379 处持有主节点的副本
请求路由到 7381 服务时隙 12182 处的节点
请求路由到 7379 服务时隙 5061 处的节点
请求路由到节点 7379, 7380, 7381 → [thing1, thing2]
请求路由到节点 7379 → [thing2]
请求路由到节点 7380 → []
请求路由到节点 7381 → [thing1]
请求路由到节点 7382 → [thing2]
当所有键都映射到同一个插槽时,本机驱动程序库会自动提供跨插槽请求,例如MGET. 但是,如果情况并非如此,RedisClusterConnection则GET针对时隙服务节点运行多个并行命令并再次返回累积结果。这比单槽方法性能低,因此应谨慎使用。如果有疑问,请考虑通过在大括号中提供前缀(例如{my-prefix}.thing1和 )将密钥固定到同一插槽{my-prefix}.thing2,这将映射到相同的插槽编号。以下示例显示了跨槽请求处理:
示例 7. 跨时隙请求处理示例
redis-cli@127.0.0.1:7379 > cluster nodes
6b38bb... 127.0.0.1:7379 master - 0 0 25 connected 0-5460
7bb...
RedisClusterConnection connection = connectionFactory.getClusterConnnection();
connection.set("thing1", value); // slot: 12182
connection.set("{thing1}.thing2", value); // slot: 12182
connection.set("thing2", value); // slot: 5461
connection.mGet("thing1", "{thing1}.thing2");
connection.mGet("thing1", "thing2");
与之前示例中的配置相同。
键映射到同一插槽 → 127.0.0.1:7381 MGET thing1 {thing1}.thing2
键映射到不同的插槽并被拆分为路由到相应节点的单个插槽
→ 127.0.0.1:7379 GET thing2
→ 127.0.0.1:7381 GET thing1
前面的示例演示了 Spring Data Redis 遵循的一般策略。请注意,某些操作可能需要将大量数据加载到内存中才能计算所需的命令。此外,并非所有跨时隙请求都可以安全地移植到多个单时隙请求中,如果误用(例如,PFCOUNT)会出错。
12.3.与RedisTemplate和ClusterOperations
请参阅通过 RedisTemplate 处理对象部分,了解有关RedisTemplate.
RedisTemplate#keySerializer使用任何 JSON 进行 设置时要小心RedisSerializers,因为更改 JSON 结构会对哈希槽计算产生直接影响。
RedisTemplate通过ClusterOperations接口提供对集群特定操作的访问,该接口可以从
RedisTemplate.opsForCluster(). 这使您可以在集群内的单个节点上显式运行命令,同时保留为模板配置的序列化和反序列化功能。它还提供管理命令(例如CLUSTER MEET)或更高级的操作(例如重新分片)。
下面的例子演示如何访问RedisClusterConnection使用RedisTemplate:
实施例8.访问RedisClusterConnection与RedisTemplate
ClusterOperations clusterOps = redisTemplate.opsForCluster();
clusterOps.shutdown(NODE_7379);
在 7379 关闭节点,交叉手指有一个可以接管的副本。
共同学习,写下你的评论
评论加载中...
作者其他优质文章