以前介紹的幾篇redis的博文都是基於單機的redis基礎上進行演示說明的,然而在實際的生產環境中,使用redis集羣的可能性應該是大於單機版的redis的,那麼集羣的redis如何操做呢?它的配置和單機的有什麼區別,又有什麼須要注意的呢?java
本篇將主要介紹SpringBoot項目整合redis集羣,並針對這個過程當中出現的問題進行說明,並給出相應的解決方案node
<!-- more -->git
首先須要安裝redis集羣環境,能夠參考博文:redis-集羣搭建手冊github
而後初始化springboot項目,對應的pom結構以下redis
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.7</version> <relativePath/> <!-- lookup parent from update --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <spring-cloud.version>Finchley.RELEASE</spring-cloud.version> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> </dependency> </dependencies> <build> <pluginManagement> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </pluginManagement> </build> <repositories> <repository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://repo.spring.io/milestone</url> <snapshots> <enabled>false</enabled> </snapshots> </repository> </repositories
須要注意的是,咱們引入了兩個包,一個是必要的 spring-boot-starter-data-redis
,官方封裝的一個操做redis的start工具,藉助它咱們能夠很方便的直接使用RedisTemplate來操做redisspring
另一個是commonos-pool2
這個包,主要是當咱們配置了redis的鏈接池的時候,須要用到它,不然會拋一個Class Not Found 的異常apache
這裏我將redis集羣搭建在局域網內的一臺centos機器上,從後面的配置文件也能夠看出(爲何這麼處理?主要是爲了引出後面一個問題)centos
首咱們先按照默認的配置方式,來獲取咱們的 RedisTemplate
, 以實現最快的接入redis集羣springboot
spring: redis: password: cluster: nodes: 192.168.0.203:7000,192.168.0.203:7001,192.168.0.203:7002 max-redirects: 3 lettuce: pool: max-idle: 16 max-active: 32 min-idle: 8
咱們搭建的redis集羣,沒有作主備(不然須要6個實例),爲了省事,也沒有設置密碼(生產環境下,這是嚴格禁止的)bash
由於咱們採用默認的配置,所以能夠直接獲取RedisTemplate的bean對象,來操做redis集羣
@SpringBootApplication public class Application { public Application(RedisTemplate redisTemplate) { redisTemplate.opsForValue().set("spring-r-cluster-1", 123); redisTemplate.opsForValue().set("spring-r-cluster-2", 456); redisTemplate.opsForValue().set("spring-r-cluster-3", 789); } public static void main(String[] args) { SpringApplication.run(Application.class); } }
上面執行以後,報的第一個錯誤是鏈接拒絕,而我在redis集羣所在的機器(203)上是能夠鏈接成功的,可是本機鏈接報錯
出現上面的問題,通常有兩個緣由,一個是防火牆致使端口不能外部訪問,一個是redis的配置
防火牆的確認方式
firewall-cmd --state
若是提示not running 表示未開啓firewall-cmd --list-all
而後能夠根據實際場景,添加端口
# 永久開啓7000端口的公共訪問權限 sudo firewall-cmd --zone=public --add-port=7000/tcp --permanent sudo firewall-cmd --reload
固然在內網的測試環境下,能夠直接關閉防火牆
//Disable firewall systemctl disable firewalld systemctl stop firewalld systemctl status firewalld //Enable firewall systemctl enable firewalld systemctl start firewalld systemctl status firewalld
redis配置
若是確認不是防火牆問題,那麼多半是redis的配置須要修改一下了,在redis.conf
中,有一行bind 127.0.0.1
配置默認開啓,表示只容許本機訪問,其餘機器無權訪問
解決辦法就是修改一下這個配置,並重啓
bind 0.0.0.0
執行前面的測試用例時,發現會拋一個奇怪的異常以下
關鍵堆棧信息以下
Caused by: org.springframework.data.redis.RedisSystemException: Redis exception; nested exception is io.lettuce.core.RedisException: io.lettuce.core.RedisConnectionException: Unable to connect to 127.0.0.1:7001 at org.springframework.data.redis.connection.lettuce.LettuceExceptionConverter.convert(LettuceExceptionConverter.java:74) ~[spring-data-redis-2.0.9.RELEASE.jar:2.0.9.RELEASE] at org.springframework.data.redis.connection.lettuce.LettuceExceptionConverter.convert(LettuceExceptionConverter.java:41) ~[spring-data-redis-2.0.9.RELEASE.jar:2.0.9.RELEASE] at org.springframework.data.redis.PassThroughExceptionTranslationStrategy.translate(PassThroughExceptionTranslationStrategy.java:44) ~[spring-data-redis-2.0.9.RELEASE.jar:2.0.9.RELEASE] at org.springframework.data.redis.FallbackExceptionTranslationStrategy.translate(FallbackExceptionTranslationStrategy.java:42) ~[spring-data-redis-2.0.9.RELEASE.jar:2.0.9.RELEASE] at org.springframework.data.redis.connection.lettuce.LettuceConnection.convertLettuceAccessException(LettuceConnection.java:257) ~[spring-data-redis-2.0.9.RELEASE.jar:2.0.9.RELEASE] at org.springframework.data.redis.connection.lettuce.LettuceStringCommands.convertLettuceAccessException(LettuceStringCommands.java:718) ~[spring-data-redis-2.0.9.RELEASE.jar:2.0.9.RELEASE] at org.springframework.data.redis.connection.lettuce.LettuceStringCommands.set(LettuceStringCommands.java:143) ~[spring-data-redis-2.0.9.RELEASE.jar:2.0.9.RELEASE] at org.springframework.data.redis.connection.DefaultedRedisConnection.set(DefaultedRedisConnection.java:231) ~[spring-data-redis-2.0.9.RELEASE.jar:2.0.9.RELEASE] at org.springframework.data.redis.core.DefaultValueOperations$3.inRedis(DefaultValueOperations.java:202) ~[spring-data-redis-2.0.9.RELEASE.jar:2.0.9.RELEASE] at org.springframework.data.redis.core.AbstractOperations$ValueDeserializingRedisCallback.doInRedis(AbstractOperations.java:59) ~[spring-data-redis-2.0.9.RELEASE.jar:2.0.9.RELEASE] at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:224) ~[spring-data-redis-2.0.9.RELEASE.jar:2.0.9.RELEASE] at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:184) ~[spring-data-redis-2.0.9.RELEASE.jar:2.0.9.RELEASE] at org.springframework.data.redis.core.AbstractOperations.execute(AbstractOperations.java:95) ~[spring-data-redis-2.0.9.RELEASE.jar:2.0.9.RELEASE] at org.springframework.data.redis.core.DefaultValueOperations.set(DefaultValueOperations.java:198) ~[spring-data-redis-2.0.9.RELEASE.jar:2.0.9.RELEASE] at com.git.hui.boot.redis.cluster.Application.<init>(Application.java:14) [classes/:na] at com.git.hui.boot.redis.cluster.Application$$EnhancerBySpringCGLIB$$ac0c03ba.<init>(<generated>) ~[classes/:na] at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) ~[na:1.8.0_171] at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) ~[na:1.8.0_171] at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) ~[na:1.8.0_171] at java.lang.reflect.Constructor.newInstance(Constructor.java:423) ~[na:1.8.0_171] at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:170) ~[spring-beans-5.0.8.RELEASE.jar:5.0.8.RELEASE] ... 19 common frames omitted Caused by: io.lettuce.core.RedisException: io.lettuce.core.RedisConnectionException: Unable to connect to 127.0.0.1:7001 at io.lettuce.core.LettuceFutures.awaitOrCancel(LettuceFutures.java:125) ~[lettuce-core-5.0.4.RELEASE.jar:na] at io.lettuce.core.cluster.ClusterFutureSyncInvocationHandler.handleInvocation(ClusterFutureSyncInvocationHandler.java:118) ~[lettuce-core-5.0.4.RELEASE.jar:na] at io.lettuce.core.internal.AbstractInvocationHandler.invoke(AbstractInvocationHandler.java:80) ~[lettuce-core-5.0.4.RELEASE.jar:na] at com.sun.proxy.$Proxy44.set(Unknown Source) ~[na:na] at org.springframework.data.red
經過斷點能夠看到,集羣中的節點ip/端口是準確的,可是異常提示出來個沒法鏈接127.0.0.1:7001
,出現這個問題的緣由,主要是咱們在建立redis集羣的時候,設置集羣節點使用以下面的命令
redis/src/redis-cli --cluster create 127.0.0.1:7000 127.0.0.1:7001 127.0.0.1:7002
經過上面這種方式建立的redis集羣,並無什麼問題,可是在springbot的整合中,經過redis集羣獲取到的節點信息就是127.0.0.1:7000
... 而後致使上面的問題,所以一個解決辦法是在建立集羣的時候,指定下ip
首先數據和配置,而後從新創建集羣關係
# 刪除數據配置 rm xxx/data/* redis/src/redis-cli --cluster create 192.168.0.203:7000 192.168.0.203:7001 192.168.0.203:7002
而後再次測試ok
前面的配置默認會使用letttuce做爲redis的橋接工具,若是咱們底層想使用jedis,能夠怎麼操做?
首先在pom依賴中添加jedis依賴
<dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> </dependency>
yml文件中的配置基本上不改都ok,在實際的項目中,對鏈接池稍微改了一下,不影響閱讀,這裏不貼出
接下來是定義RedisConnectionFactoy
來替換默認的
下面的配置和之前的一篇博文 181101-SpringBoot高級篇Redis之Jedis配置 基本差很少,須要注意的是咱們使用
RedisClusterConfiguration
替換了RedisStandaloneConfiguration
@Configuration public class RedisAutoConfig { @Bean public RedisConnectionFactory redisConnectionFactory(JedisPoolConfig jedisPool, RedisClusterConfiguration jedisConfig) { JedisConnectionFactory factory = new JedisConnectionFactory(jedisConfig, jedisPool); factory.afterPropertiesSet(); return factory; } @Configuration public static class JedisConf { @Value("${spring.redis.cluster.nodes:127.0.0.1:7000,127.0.0.1:7001,127.0.0.1:7002}") private String nodes; @Value("${spring.redis.cluster.max-redirects:3}") private Integer maxRedirects; @Value("${spring.redis.password:}") private String password; @Value("${spring.redis.database:0}") private Integer database; @Value("${spring.redis.jedis.pool.max-active:8}") private Integer maxActive; @Value("${spring.redis.jedis.pool.max-idle:8}") private Integer maxIdle; @Value("${spring.redis.jedis.pool.max-wait:-1}") private Long maxWait; @Value("${spring.redis.jedis.pool.min-idle:0}") private Integer minIdle; @Bean public JedisPoolConfig jedisPool() { JedisPoolConfig jedisPoolConfig = new JedisPoolConfig(); jedisPoolConfig.setMaxIdle(maxIdle); jedisPoolConfig.setMaxWaitMillis(maxWait); jedisPoolConfig.setMaxTotal(maxActive); jedisPoolConfig.setMinIdle(minIdle); return jedisPoolConfig; } @Bean public RedisClusterConfiguration jedisConfig() { RedisClusterConfiguration config = new RedisClusterConfiguration(); String[] sub = nodes.split(","); List<RedisNode> nodeList = new ArrayList<>(sub.length); String[] tmp; for (String s : sub) { tmp = s.split(":"); // fixme 先不考慮異常配置的case nodeList.add(new RedisNode(tmp[0], Integer.valueOf(tmp[1]))); } config.setClusterNodes(nodeList); config.setMaxRedirects(maxRedirects); config.setPassword(RedisPassword.of(password)); return config; } } }
而後其餘的依舊,此時RedisTemplate的底層鏈接就變成了Jedis
關聯博文
盡信書則不如,以上內容,純屬一家之言,因我的能力有限,不免有疏漏和錯誤之處,如發現bug或者有更好的建議,歡迎批評指正,不吝感激
下面一灰灰的我的博客,記錄全部學習和工做中的博文,歡迎你們前去逛逛