springboot之如何同時(shí)連接多個(gè)redis
線上服務(wù)需要連接三個(gè)redis服務(wù)器;業(yè)務(wù)背景不能介紹,直接上代碼:
技術(shù)選型
Springboot連接reids的三個(gè)客戶端
Jedis:是Redis的Java實(shí)現(xiàn)客戶端,提供了比較全面的Redis命令的支持,復(fù)雜的redis操作需要使用它;springboot1.x 默認(rèn)集成;據(jù)說在高并發(fā)下有并發(fā)性問題出現(xiàn);
Lettuce:高級(jí)Redis客戶端,用于線程安全同步,異步和響應(yīng)使用,支持集群,Sentinel,管道和編碼器,springboot 2.x 默認(rèn)集成
Redission:Redisson是一個(gè)在Redis的基礎(chǔ)上實(shí)現(xiàn)的Java駐內(nèi)存數(shù)據(jù)網(wǎng)格(In-Memory Data Grid)。它不僅提供了一系列的分布式的Java常用對(duì)象,還提供了許多分布式服務(wù)。其中包括(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的宗旨是促進(jìn)使用者對(duì)Redis的關(guān)注分離(Separation of Concern),從而讓使用者能夠?qū)⒕Ω械胤旁谔幚順I(yè)務(wù)邏輯上。暫時(shí)企業(yè)級(jí)開發(fā)感覺只是使用了分布式鎖;
結(jié)論:
單個(gè)redis隨便使用哪個(gè)客戶端都可以,也可以使用 Jedis + Redission 或者 Lettuce + Redission;
由于Jedis使用和研究比較多,此處使用Jedis拋磚引玉,實(shí)現(xiàn)三組redis + 分布式鎖;Lettuce版本也可以根據(jù)此思路編寫;
代碼部分
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配置 三個(gè)redis同時(shí)存在 * @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("==============請(qǐng)?zhí)砑觬edis配置================"); 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不可用,請(qǐng)檢查組件是否啟動(dòng):",e); } finally { connection.close(); } return jedisConnectionFactory; } /** * poolConfig連接池配置 只有集群時(shí)使用 直接寫死,不讓外部配置了 * @return */ public JedisPoolConfig poolConfig() { JedisPoolConfig config = new JedisPoolConfig(); config.setMaxTotal(200); config.setMaxIdle(50); config.setMinIdle(8); config.setMaxWaitMillis(10000); // 獲取連接時(shí)的最大等待毫秒數(shù)(如果設(shè)置為阻塞時(shí)BlockWhenExhausted),如果超時(shí)就拋異常, 小于零:阻塞不確定的時(shí)間, 默認(rèn)-1 config.setTestOnBorrow(true); // 在獲取連接的時(shí)候檢查有效性, 默認(rèn)false config.setTestOnReturn(false); // 調(diào)用returnObject方法時(shí),是否進(jìn)行有效檢查 config.setTestWhileIdle(true); // Idle時(shí)進(jìn)行連接掃描 config.setTimeBetweenEvictionRunsMillis(30000); // 表示idle object evitor兩次掃描之間要sleep的毫秒數(shù) config.setNumTestsPerEvictionRun(10); // 表示idle object evitor每次掃描的最多的對(duì)象數(shù) config.setMinEvictableIdleTimeMillis(60000); // 表示一個(gè)對(duì)象至少停留在idle狀態(tài)的最短時(shí)間,然后才能被idle object evitor掃描并驅(qū)逐;這一項(xiàng)只有在timeBetweenEvictionRunsMillis大于0時(shí)才有意義 return config; } /** * redisStandaloneConfiguration 單機(jī)版配置 * @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信息 結(jié)束==============================="); 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信息 結(jié)束==============================="); 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信息 結(jié)束==============================="); return template; } }
其中在connectionFactory方法中,添加了,自動(dòng)檢查redis是否連接成功的代碼,在啟動(dòng)項(xiàng)目時(shí)即可判斷是否連接成功。
啟動(dòng)失敗日志
啟動(dòng)成功日志
類中使用
@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"; } }
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
詳解SpringBoot上傳圖片到阿里云的OSS對(duì)象存儲(chǔ)中
這篇文章主要介紹了SpringBoot上傳圖片到阿里云的OSS對(duì)象存儲(chǔ)中,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-10-10一篇文章掌握J(rèn)ava?Thread的類及其常見方法
Thread類用于操作線程,是所以涉及到線程操作(如并發(fā))的基礎(chǔ)。本文將通過代碼對(duì)Thread類的功能作用及其常見方法進(jìn)行分析2022-03-03Java后綴數(shù)組之求sa數(shù)組的實(shí)例代碼
后綴數(shù)組就是一個(gè)字符串所有后綴大小排序后的一個(gè)集合,然后我們根據(jù)后綴數(shù)組的一些性質(zhì)就可以實(shí)現(xiàn)各種需求。這篇文章主要介紹了Java后綴數(shù)組-求sa數(shù)組,需要的朋友可以參考下2018-04-04剖析Java中阻塞隊(duì)列的實(shí)現(xiàn)原理及應(yīng)用場景
這篇文章主要介紹了剖析Java中阻塞隊(duì)列的實(shí)現(xiàn)原理及應(yīng)用場景,這里也對(duì)阻塞和非阻塞隊(duì)列的不同之處進(jìn)行了對(duì)比,需要的朋友可以參考下2015-12-12Java從源碼看異步任務(wù)計(jì)算FutureTask
這篇文章主要介紹了Java從源碼看異步任務(wù)計(jì)算FutureTask,F(xiàn)utureTask就能夠很好的幫助我們實(shí)現(xiàn)異步計(jì)算,并且可以實(shí)現(xiàn)同步獲取異步任務(wù)的計(jì)算結(jié)果,具體是怎樣實(shí)現(xiàn)的,下面我們就一起來學(xué)習(xí)下面文章的具體內(nèi)容吧2022-04-04