Redis之Java客户端连接

发布时间 2023-09-26 18:38:22作者: strongmore

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等。我们如果想自定义脚本,也必须使用这种方式,确保在集群模式下不出错。