Spring整合Redis
使用Lettuce框架访问Redis
private static RedisCommands<String, String> createRedisCommands() {
RedisURI.Builder builder = RedisURI.Builder.redis("", 6379);
builder.withPassword("test123").withDatabase(1);
RedisClient redisClient = RedisClient.create(builder.build());
StatefulRedisConnection<String, String> connection = redisClient.connect();
return connection.sync();
}
使用spring-data-redis访问Redis
private static LettuceConnectionFactory createConnectionFactory() {
RedisStandaloneConfiguration standaloneConfiguration = new RedisStandaloneConfiguration();
standaloneConfiguration.setDatabase(1);
standaloneConfiguration.setHostName("");
standaloneConfiguration.setPassword("test123");
LettuceConnectionFactory connectionFactory = new LettuceConnectionFactory(
standaloneConfiguration);
connectionFactory.afterPropertiesSet();
return connectionFactory;
}
private static StringRedisTemplate createStringRedisTemplate() {
LettuceConnectionFactory connectionFactory = createConnectionFactory();
return new StringRedisTemplate(connectionFactory);
}
消息发布
// 消息发布
StringRedisTemplate redisTemplate = createStringRedisTemplate();
redisTemplate.convertAndSend("test1", "hello");
消息订阅
private static RedisMessageListenerContainer createMessageListenerContainer(
RedisConnectionFactory connectionFactory, MessageListener messageListener) {
RedisMessageListenerContainer listenerContainer = new RedisMessageListenerContainer();
listenerContainer.setConnectionFactory(connectionFactory);
listenerContainer.addMessageListener(messageListener, new ChannelTopic("test1"));
listenerContainer.afterPropertiesSet();
listenerContainer.start();
return listenerContainer;
}
可以自己实现 MessageListener 接口,也可以使用MessageListenerAdapter委托
static class MyMessageListener implements MessageListener {
@Override
public void onMessage(Message message, byte[] pattern) {
System.out.println(new String(message.getBody()));
}
}
static class MyMessageListener extend MessageListenerAdapter {
//方法名称固定,参数也是固定,必须为public
public void handleMessage(String text, String channel) {
System.out.println(text);
System.out.println(channel);
}
}
使用Redisson连接Redis
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>3.16.0</version>
<exclusions>
<exclusion>
<groupId>org.glassfish</groupId>
<artifactId>jakarta.el</artifactId>
</exclusion>
</exclusions>
</dependency>
RedissonAutoConfiguration自动配置了RedissonClient实现。
Redisson使用哨兵模式连接服务器
private static RedissonClient createRedissonSentinelClient() {
//通过哨兵获取到master节点的实际地址,请求此节点
Config config = new Config();
SentinelServersConfig sentinelServersConfig = config.useSentinelServers();
sentinelServersConfig.setSentinelAddresses(Arrays.asList(
"redis://ip:26380",
"redis://ip:26381",
"redis://ip:26382"
));
sentinelServersConfig.setMasterName("mymaster");
//报错 SENTINEL SENTINELS command returns less than 2 nodes 具体原因未知
sentinelServersConfig.setCheckSentinelsList(false);
return Redisson.create(config);
}
主要需要配置哨兵的地址和master名称,Redisson内部会定时和哨兵服务通信,检查master节点是否有变更。
Redisson使用集群模式连接服务器
cluster slots # 查询slot在所有节点的分布情况
cluster keyslot key # 计算key对应的slot
具体计算slot的算法为 CRC16 % 16384,Redisson中通过ClusterConnectionManager的calcSlot()方法来实现。如果一个key格式为
cluster keyslot prefix:{hash_tag}:suffix
那么会取其中的hash_tag来计算slot,这个可以实现多个不同的key具有相同的slot
cluster keyslot user:{10086}:friends
cluster keyslot user:{10086}:videos
结果都为 5466 = CRC16("10086") % 16384
private static RedissonClient createRedissonClusterClient(){
Config config = new Config();
ClusterServersConfig clusterServersConfig = config.useClusterServers();
clusterServersConfig.setNodeAddresses(Arrays.asList(
"redis://ip:6371",
"redis://ip:6372",
"redis://ip:6373",
"redis://ip:6374",
"redis://ip:6375",
"redis://ip:6376"
));
clusterServersConfig.setPassword("test123");
//报错 Not all slots covered! Only 10923 slots are available.
clusterServersConfig.setCheckSlotsCoverage(false);
return Redisson.create(config);
}
通过 cluster nodes 命令获取到所有节点及对应的slot范围。创建slot->node的对应关系,实际执行命令时,先根据key计算出slot,再根据对应关系找到要执行的节点。
Redisson内部会定时和集群服务通信,检查节点是否有增加或删除,来调整slot的对应关系。
Redisson 对lua脚本的处理
Lua和事务需要操作的key,必须在同一个节点上,Redis Cluster
提供了hashtag,Redisson内部的lua脚本就使用了这种特性,如RedissonBloomFilter,RedissonIdGenerator等。我们如果想自定义脚本,也必须使用这种方式,确保在集群模式下不出错。