springboot之如何同时连接多个redis
作者:窦再兴
线上服务需要连接三个redis服务器;业务背景不能介绍,直接上代码:
技术选型
Springboot连接reids的三个客户端
Jedis:是Redis的Java实现客户端,提供了比较全面的Redis命令的支持,复杂的redis操作需要使用它;springboot1.x 默认集成;据说在高并发下有并发性问题出现;
Lettuce:高级Redis客户端,用于线程安全同步,异步和响应使用,支持集群,Sentinel,管道和编码器,springboot 2.x 默认集成
Redission:Redisson是一个在Redis的基础上实现的Java驻内存数据网格(In-Memory Data Grid)。它不仅提供了一系列的分布式的Java常用对象,还提供了许多分布式服务。其中包括(BitSet, Set, Multimap, SortedSet, Map, List, Queue, BlockingQueue, Deque, BlockingDeque, Semaphore, Lock, AtomicLong, CountDownLatch, Publish / Subscribe, Bloom filter, Remote service, Spring cache, Executor service, Live Object service, Scheduler service) Redisson提供了使用Redis的最简单和最便捷的方法。Redisson的宗旨是促进使用者对Redis的关注分离(Separation of Concern),从而让使用者能够将精力更集中地放在处理业务逻辑上。暂时企业级开发感觉只是使用了分布式锁;
结论:
单个redis随便使用哪个客户端都可以,也可以使用 Jedis + Redission 或者 Lettuce + Redission;
由于Jedis使用和研究比较多,此处使用Jedis抛砖引玉,实现三组redis + 分布式锁;Lettuce版本也可以根据此思路编写;
代码部分
maven pom引用
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> <exclusions> <!-- 不依赖Redis的异步客户端lettuce --> <exclusion> <groupId>io.lettuce</groupId> <artifactId>lettuce-core</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> </dependency>
application.yml配置
spring: redis: r1: host: 192.168.1.210 port: 6379 password: #cluster: #nodes: 192.168.1.101:6379,192.168.1.102:6379,192.168.1.103:6379 r2: host: 192.168.1.211 port: 6379 password: #cluster: #nodes: 192.168.1.104:6379,192.168.1.105:6379,192.168.1.106:6379 r3: host: 192.168.1.212 port: 6379 password: #cluster: #nodes: 192.168.1.107:6379,192.168.1.108:6379,192.168.1.109:6379
Configuration代码
import java.util.HashSet; import java.util.Set; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisClusterConfiguration; import org.springframework.data.redis.connection.RedisConnection; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.connection.RedisNode; import org.springframework.data.redis.connection.RedisPassword; import org.springframework.data.redis.connection.RedisStandaloneConfiguration; import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import cn.hutool.core.util.StrUtil; import lombok.extern.slf4j.Slf4j; import redis.clients.jedis.JedisPoolConfig; /** * redis配置 三个redis同时存在 * @author douzi * @date 2021-12-2 09:00:00 */ @Slf4j @Configuration public class RedisJedisConfig2 { // r1 redis 配置信息 @Value("${spring.redis.r1.host:}") private String r1Host; @Value("${spring.redis.r1.port:}") private Integer r1Port; @Value("${spring.redis.r1.password:}") private String r1Password; @Value("${spring.redis.r1.cluster.nodes:}") private String r1Nodes; //r2 redis 配置信息 @Value("${spring.redis.r2.host:}") private String r2Host; @Value("${spring.redis.r2.port:}") private Integer r2Port; @Value("${spring.redis.r2.password:}") private String r2Password; @Value("${spring.redis.r2.cluster.nodes:}") private String r2Nodes; //r3 redis 配置信息 @Value("${spring.redis.r3.host:}") private String r3Host; @Value("${spring.redis.r3.port:}") private Integer r3Port; @Value("${spring.redis.r3.password:}") private String r3Password; @Value("${spring.redis.r3.cluster.nodes:}") private String r3Nodes; /** * connectionFactory 配置工厂 */ public RedisConnectionFactory connectionFactory( RedisStandaloneConfiguration redisStandaloneConfiguration, RedisClusterConfiguration redisClusterConfiguration, JedisPoolConfig jedisPoolConfig) { if (redisStandaloneConfiguration == null && redisClusterConfiguration == null) { log.error("==============请添加redis配置================"); return null; } JedisConnectionFactory jedisConnectionFactory = null; if (redisStandaloneConfiguration != null) { jedisConnectionFactory = new JedisConnectionFactory(redisStandaloneConfiguration); } else { jedisConnectionFactory = new JedisConnectionFactory(redisClusterConfiguration, jedisPoolConfig); } jedisConnectionFactory.afterPropertiesSet(); // 检查是否可用 RedisConnection connection = null; try { connection = jedisConnectionFactory.getConnection(); log.info("reids是否可用:" + !connection.isClosed()); } catch(Exception e) { log.error("reids不可用,请检查组件是否启动:",e); } finally { connection.close(); } return jedisConnectionFactory; } /** * poolConfig连接池配置 只有集群时使用 直接写死,不让外部配置了 * @return */ public JedisPoolConfig poolConfig() { JedisPoolConfig config = new JedisPoolConfig(); config.setMaxTotal(200); config.setMaxIdle(50); config.setMinIdle(8); config.setMaxWaitMillis(10000); // 获取连接时的最大等待毫秒数(如果设置为阻塞时BlockWhenExhausted),如果超时就抛异常, 小于零:阻塞不确定的时间, 默认-1 config.setTestOnBorrow(true); // 在获取连接的时候检查有效性, 默认false config.setTestOnReturn(false); // 调用returnObject方法时,是否进行有效检查 config.setTestWhileIdle(true); // Idle时进行连接扫描 config.setTimeBetweenEvictionRunsMillis(30000); // 表示idle object evitor两次扫描之间要sleep的毫秒数 config.setNumTestsPerEvictionRun(10); // 表示idle object evitor每次扫描的最多的对象数 config.setMinEvictableIdleTimeMillis(60000); // 表示一个对象至少停留在idle状态的最短时间,然后才能被idle object evitor扫描并驱逐;这一项只有在timeBetweenEvictionRunsMillis大于0时才有意义 return config; } /** * redisStandaloneConfiguration 单机版配置 * @param host * @param port * @param password * @param index * @return */ public RedisStandaloneConfiguration redisStandaloneConfiguration(String host, int port, String password, int index) { RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration(host, port); if (StrUtil.isNotBlank(password)) { redisStandaloneConfiguration.setPassword(password); } if (index != 0) { redisStandaloneConfiguration.setDatabase(index); } return redisStandaloneConfiguration; } /** * redisClusterConfiguration 集群配置 * @param clusterNodes * @param password * @return */ public RedisClusterConfiguration redisClusterConfiguration(String clusterNodes, String password) { RedisClusterConfiguration redisClusterConfiguration = new RedisClusterConfiguration(); // Set<RedisNode> clusterNodes String[] serverArray = clusterNodes.split(","); Set<RedisNode> nodes = new HashSet<RedisNode>(); for (String ipPort : serverArray) { String[] ipAndPort = ipPort.split(":"); nodes.add(new RedisNode(ipAndPort[0].trim(), Integer.valueOf(ipAndPort[1]))); } redisClusterConfiguration.setClusterNodes(nodes); redisClusterConfiguration.setMaxRedirects(6); if (StrUtil.isNotBlank(password)) { redisClusterConfiguration.setPassword(RedisPassword.of(password)); } return redisClusterConfiguration; } @Bean(name = "redisR1Template") public RedisTemplate<String, Object> redisR1Template() { RedisTemplate<String, Object> template = new RedisTemplate<>(); RedisStandaloneConfiguration redisStandaloneConfiguration = null; RedisClusterConfiguration redisClusterConfiguration = null; if (StrUtil.isNotBlank(r1Host) && StrUtil.isBlank(r1Nodes)) { redisStandaloneConfiguration = redisStandaloneConfiguration(r1Host, r1Port, r1Password, 0); } else if (StrUtil.isNotBlank(r1Nodes)) { redisClusterConfiguration = redisClusterConfiguration(r1Nodes, r1Password); } log.info("=========================R1 redis信息 开始==============================="); template.setConnectionFactory(connectionFactory(redisStandaloneConfiguration, redisClusterConfiguration, poolConfig())); log.info("=========================R1 redis信息 结束==============================="); return template; } @Bean(name = "redisR2Template") public RedisTemplate<String, Object> redisR2Template() { RedisTemplate<String, Object> template = new RedisTemplate<>(); RedisStandaloneConfiguration redisStandaloneConfiguration = null; RedisClusterConfiguration redisClusterConfiguration = null; if (StrUtil.isNotBlank(r2Host) && StrUtil.isBlank(r2Nodes)) { redisStandaloneConfiguration = redisStandaloneConfiguration(r2Host, r2Port, r2Password, 0); } else if (StrUtil.isNotBlank(r2Nodes)) { redisClusterConfiguration = redisClusterConfiguration(r2Nodes, r2Password); } log.info("=========================R2 redis信息 开始==============================="); template.setConnectionFactory(connectionFactory(redisStandaloneConfiguration, redisClusterConfiguration, poolConfig())); log.info("=========================R2 redis信息 结束==============================="); return template; } @Bean(name = "redisR3Template") public RedisTemplate<String, Object> redisR3Template() { RedisTemplate<String, Object> template = new RedisTemplate<>(); RedisStandaloneConfiguration redisStandaloneConfiguration = null; RedisClusterConfiguration redisClusterConfiguration = null; if (StrUtil.isNotBlank(r3Host) && StrUtil.isBlank(r3Nodes)) { redisStandaloneConfiguration = redisStandaloneConfiguration(r3Host, r3Port, r3Password, 0); } else if (StrUtil.isNotBlank(r3Nodes)) { redisClusterConfiguration = redisClusterConfiguration(r3Nodes, r3Password); } log.info("=========================R3 redis信息 开始==============================="); template.setConnectionFactory(connectionFactory(redisStandaloneConfiguration, redisClusterConfiguration, poolConfig())); log.info("=========================R3 redis信息 结束==============================="); return template; } }
其中在connectionFactory方法中,添加了,自动检查redis是否连接成功的代码,在启动项目时即可判断是否连接成功。
启动失败日志
启动成功日志
类中使用
@RestController @RequestMapping("/redis") public class TestRedisController { @Autowired RedisTemplate<String, Object> redisR1Template; @Autowired RedisTemplate<String, Object> redisR2Template; @Autowired RedisTemplate<String, Object> redisR3Template; @GetMapping("/cs") public String test() { redisR1Template.opsForValue().get("1"); redisR2Template.opsForValue().get("1"); redisR3Template.opsForValue().get("1"); return "1"; } }
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。