1、问题背景
在一个Spring boot项目中,需要使用redis作为缓存,于是将使用spring-boot-starter-data-redis,具体依赖如下:
org.springframework.boot spring-boot-starter-data-redis 2.0.4.RELEASE
在测试环境中,功能测试,压力测试,都没有发现问题,原因是测试环境中redis自行搭建,没有设置密码,但是上线后,Redis使用的是A***的Pass服务的集群,并设置密码,使用过程中发现如下问题:
(资料图片仅供参考)
com.lambdaworks.redis.RedisException: java.lang.IllegalArgumentException: Connection to XXX.XX.XXX.XXX:15000 not allowed. This connection point is not known in the cluster viewjava.lang.IllegalArgumentException: Connection to XXX.XX.XXX.XXX:15000 not allowed. This connection point is not known in the cluster viewConnection to XXX.XX.XXX.XXX:15000 not allowed. This connection point is not known in the cluster view
2、问题分析+解决方法
2.1、redis负载过高问题
2.1.1、问题原因
原本打算看一下是否是代码逻辑问题导致redis负载过高,于是登录redis服务器使用monitor命令观察命令执行的频率,发现每执行一次命令都执行一次Auth password
命令,说明连接池未正确使用导致执行一次命令创建一次连接,导致负载高 ,并且代码执行效率低 。
2.1.2、解决方法
然后对比了使用JedisCluster的项目没有此类问题,因此怀疑是spring-boot-starter-data-redis的RedisTemplate的问题,查看源码后发现spring-data-redis的驱动包在某个版本之后替换为 Lettuce,在启用集群后jedis的连接池无效。错误配置如下:
# 错误配置# Redis配置spring.redis.cluster.nodes=127.0.0.1:6379### 连接超时时间(毫秒)spring.redis.timeout=60000spring.redis.password=xxxxxxx# 连接池最大连接数(使用负值表示没有限制)spring.redis.jedis.pool.max-active=8##连接池最大阻塞等待时间,若使用负值表示没有限制spring.redis.jedis.pool.max-wait=-1##连接池中的最大空闲连接spring.redis.jedis.pool.max-idle=8# 连接池中的最小空闲连接spring.redis.jedis.pool.min-idle=0
需要改成正确的配置,修改之后无此现象,具体配置如下:
单机版:
# 单机版# Redis配置spring.redis.host=127.0.0.1spring.redis.port=6379### 连接超时时间(毫秒)spring.redis.timeout=60000spring.redis.password=xxxxxxx# 连接池最大连接数(使用负值表示没有限制)spring.redis.jedis.pool.max-active=8##连接池最大阻塞等待时间,若使用负值表示没有限制spring.redis.jedis.pool.max-wait=-1##连接池中的最大空闲连接spring.redis.jedis.pool.max-idle=8# 连接池中的最小空闲连接spring.redis.jedis.pool.min-idle=0
集群版:
#集群版 # Redis配置spring.redis.cluster.nodes=127.0.0.1:6379### 连接超时时间(毫秒)spring.redis.timeout=60000spring.redis.password=xxxxxxx# 连接池最大连接数(使用负值表示没有限制)spring.redis.lettuce.pool.max-active=8##连接池最大阻塞等待时间,若使用负值表示没有限制spring.redis.lettuce.pool.max-wait=-1##连接池中的最大空闲连接spring.redis.lettuce.pool.max-idle=8# 连接池中的最小空闲连接spring.redis.lettuce.pool.min-idle=0
注意:启用集群版,需要额外添加如下依赖
org.apache.commons commons-pool2 2.8.0
2.2、redis异常 Connection to XXX.XX.XXX.XXX:15000 not allowed 问题
2.2.1、问题原因
网上搜索了一下,发现项目github上已有此问题的反馈以及解决办法github.com/lettuce-io/…,原因是由于Lettuce其中有个配置项validateClusterNodeMembership
默认是true导致;
2.2.2、解决办法
由于spring boot未能直接通过配置文件直接修改此配置,因此需要自定义Redis配置,具体代码如下: MylettuceConnectionFactory.java
package com.quison.test.config;import io.lettuce.core.AbstractRedisClient;import io.lettuce.core.cluster.ClusterClientOptions;import io.lettuce.core.cluster.ClusterTopologyRefreshOptions;import io.lettuce.core.cluster.RedisClusterClient;import org.springframework.beans.DirectFieldAccessor;import org.springframework.data.redis.connection.RedisClusterConfiguration;import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration;import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration;import java.util.concurrent.TimeUnit;public class MyLettuceConnectionFactory extends LettuceConnectionFactory { public MyLettuceConnectionFactory() { } public MyLettuceConnectionFactory(RedisClusterConfiguration redisClusterConfiguration, LettuceClientConfiguration lettuceClientConfiguration) { super(redisClusterConfiguration, lettuceClientConfiguration); } @Override public void afterPropertiesSet() { super.afterPropertiesSet(); DirectFieldAccessor accessor = new DirectFieldAccessor(this); AbstractRedisClient client = (AbstractRedisClient) accessor.getPropertyValue("client"); if(client instanceof RedisClusterClient){ RedisClusterClient clusterClient = (RedisClusterClient) client; ClusterTopologyRefreshOptions topologyRefreshOptions = ClusterTopologyRefreshOptions.builder() .enablePeriodicRefresh(10, TimeUnit.MINUTES) .enableAllAdaptiveRefreshTriggers() .build(); ClusterClientOptions clusterClientOptions = ClusterClientOptions.builder() // 注意此配置项设置为false .validateClusterNodeMembership(false) .topologyRefreshOptions(topologyRefreshOptions) .build(); clusterClient.setOptions(clusterClientOptions); } }}
由于配置后,连接池也需要自行设置,因此Redis的配置文件修改为如下设置 RedisConfig.java
package com.quison.test.config;import org.apache.commons.pool2.impl.GenericObjectPoolConfig;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.beans.factory.annotation.Value;import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;import org.springframework.boot.autoconfigure.data.redis.RedisProperties;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.Primary;import org.springframework.data.redis.connection.*;import org.springframework.data.redis.connection.lettuce.DefaultLettucePool;import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration;import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration;import org.springframework.data.redis.core.RedisTemplate;import org.springframework.data.redis.serializer.RedisSerializer;import org.springframework.data.redis.serializer.StringRedisSerializer;import java.time.Duration;import java.util.Arrays;import java.util.HashSet;import java.util.Set;@Configurationpublic class RedisConfig { @Value("${spring.redis.cluster.nodes}") private String clusterNodes; @Value("${spring.redis.password}") private String password; @Value("${spring.redis.lettuce.pool.max-idle}") private Integer maxIdle; @Value("${spring.redis.lettuce.pool.max-active}") private Integer maxActive; @Value("${spring.redis.cluster.max-redirects}") private Integer maxRedirects; @Bean public RedisConnectionFactory myRedisConnectionFactory() { RedisClusterConfiguration redisClusterConfiguration = new RedisClusterConfiguration(); String[] serverArray = clusterNodes.split(","); Set nodes = new HashSet(); for (String ipPort : serverArray) { String[] ipAndPort = ipPort.split(":"); nodes.add(new RedisNode(ipAndPort[0].trim(), Integer.valueOf(ipAndPort[1]))); } redisClusterConfiguration.setPassword(RedisPassword.of(password)); redisClusterConfiguration.setClusterNodes(nodes); redisClusterConfiguration.setMaxRedirects(maxRedirects); GenericObjectPoolConfig genericObjectPoolConfig = new GenericObjectPoolConfig(); genericObjectPoolConfig.setMaxIdle(maxIdle); genericObjectPoolConfig.setMinIdle(8); genericObjectPoolConfig.setMaxTotal(maxActive); genericObjectPoolConfig.setMaxWaitMillis(10000); LettuceClientConfiguration clientConfig = LettucePoolingClientConfiguration.builder() .commandTimeout(Duration.ofMillis(10000)) .poolConfig(genericObjectPoolConfig) .build(); return new MyLettuceConnectionFactory(redisClusterConfiguration, clientConfig); } /** * redis模板,存储关键字是字符串,值是Jdk序列化 * * @param myRedisConnectionFactory * @return * @Description: */ @Bean @ConditionalOnMissingBean(name = "redisTemplate") @Primary public RedisTemplate, ?> redisTemplate(RedisConnectionFactory myRedisConnectionFactory) { RedisTemplate, ?> redisTemplate = new RedisTemplate<>(); redisTemplate.setConnectionFactory(myRedisConnectionFactory); //key序列化方式;但是如果方法上有Long等非String类型的话,会报类型转换错误; RedisSerializer redisSerializer = new StringRedisSerializer(); redisTemplate.setKeySerializer(redisSerializer); redisTemplate.setHashKeySerializer(redisSerializer); //默认使用JdkSerializationRedisSerializer序列化方式;会出现乱码,改成StringRedisSerializer StringRedisSerializer stringSerializer = new StringRedisSerializer(); redisTemplate.setKeySerializer(stringSerializer); redisTemplate.setValueSerializer(stringSerializer); redisTemplate.setHashKeySerializer(stringSerializer); redisTemplate.setHashValueSerializer(stringSerializer); return redisTemplate; }}
3、总结
吃一堑、长一智,总结如下:
开发+测试环境尽量与线上一致,可提前发现问题;使用新技术需要多多测试再投入生产使用;关键词:
-
[Redis]Spring Boot中Redis Template集群配置
在一个Springboot项目中,需要使用redis作为缓存,于是将使用spring-boot-starter-data-redis,具体依赖如下:
-
全球速讯:ca9503航班查询_ca953航班
1、t3航站楼4层是办理登机手续的区域,其中j和k两个区时办理国内登机手续。2、h区是办理头等舱,公务舱,白金卡,金卡,
-
全球新资讯:江苏纺织服装业界人士齐聚射阳共谋奔向“春天里”
中新网江苏新闻3月18日电(陈鹏飞夏正军周洁)17日上午,由江苏省服装协会、射阳县人民政府主办,射阳县工信局、盐城市
-
新资讯:在李沁得私服中感受到了“春天”气息,还搭的很有活力,太时髦了
春天是万物复苏的季节,大片的绿色会给人一种生机勃勃的感觉,所以春季穿衣也应该如此。尤其是年轻、有活力的姑娘们,穿衣也应该选择充满朝气
-
方正小标宋体和方正小标宋简体_小标宋体和方正小标宋简体一样|今日热门
1、小标宋体字,windos里没有默认,在网上可以,最好用的是方正小标宋体。2、文件标题和主题词用,标题用二号,主题词用
-
天天日报丨北京怀柔区总工会开展党的二十大精神宣讲进基层活动
工人日报-中工网记者赖志凯通讯员阮腾飞宋雅欣 3月16日,北京市怀柔区总工会走进北京鑫霸印务有限公司开展“勇毅前行再奋楫踔厉奋发启新篇
-
电投能源:完成定增募资40亿元 中电投蒙东能源集团认购16亿元 国企结构调整基金二期等参投
电投能源3月17日发布非公开发行股票发行情况报告书,本次发行实际发行数量为3 2亿股,发行价格为12 50元 股,完成募集资金总额40亿元。扣除与
-
南大光电: 2022年度独立董事述职报告(方德才) 当前短讯
南大光电:2022年度独立董事述职报告(方德才)
-
安徽寿县淮南寿县保障性租赁住房项目正式开工 总投资6亿元
项目总用地面积约42888平方米,总建筑面积约12 6万平方米。目前该项目正按照施工计划自北向南推进建设,拟建8栋保障性住房,共1632套,包括40
-
百度股价涨超12%,文心一言这样看待股价波动 焦点讯息
百度类ChatGPT产品文心一言发布,股价一波三折。3月16日下午,在发布会进行的同时,百度港股股价一度下跌10%至120 1港元。截至昨日收盘,百度股价下跌6
-
世界滚动:事关办酒席!这里给干部下禁令,退休3年内都得执行
中新网北京3月17日电满月酒、生日宴、升学宴、乔迁宴……领导干部家中能不能操办这些酒席?如何邀请客人?近期,四川省纪委监委印发文件,对领
-
烤蓝是什么意思啊_烤蓝是什么意思|当前消息
1、烤蓝也叫发蓝处理。2、将钢材在空气或水蒸气或碱性溶液中,加热到适当温度或不加热,使其表面形成一层蓝色或黑色氧化膜的工
-
深天马A(000050):3月16日北向资金增持159.71万股
3月16日北向资金增持159 71万股深天马A。近5个交易日中,获北向资金减持的有3天,累计净减持1067 27万股。近20个交易日中,获北向资金减持的
-
世界消息!京蓉两地少年共同发起倡议:接续冬奥精神 遇见青春大运
一年前的立春之夜,以冰雪之名,世界的目光聚焦北京,中国不但让世界记住了可爱的冰墩墩,也留下了浓墨重彩的北京冬奥精神。一年后的火热盛夏
-
环球热点!常抽烟,肺不好?落实五件小事,肺功能会好起来
肺对我们的重要性,不问可知,假如肺部泛起了题目,那么,我们的健康也会受到威胁,因此平时我们一定要做好养肺工作。假如察觉到了肺部有题目
-
重庆建工:“建工转债”预计触发转股价格向下修正条件
重庆建工3月16日公告,自2023年3月8日至2023年3月16日,公司股票已至少有5个交易日的收盘价低于当期转股价格的90%(4 04元 股),预计在任意连续
-
当前快讯:2023辽宁金太阳高三3月联考各科试卷及答案汇总_23-321C
2023辽宁金太阳高三3月联考于3月15日进行考试,本文汇总了辽宁金太阳2023届高三3月联考所有科目试卷及答案,包括语
-
电脑怎么将桌面的文件默认保存到其他盘中_怎么把桌面文件默认到d盘 全球新要闻
1、打开C盘中的用户文件夹,之中有文档、视频、桌面等等,在桌面文件夹右键点击。2、选择属性,其中有位置,可设定为任意位置
-
山东安排2.8亿元支持济青等4市建设省级区域性粮食安全应急保障基地
大众网·海报新闻记者梁雯济南报道3月15日,山东省政府新闻办举行“坚定不移推动高质量发展”系列新闻发布会第十场,介绍
-
报道:起底汕头赤脚踩腌菜涉事公司 揭秘“潮汕腌制菜”生产乱象!
起底汕头赤脚踩腌菜涉事公司揭秘“潮汕腌制菜”生产乱象!今天的关注度非常高,直接上了热搜榜,那么具体的是什么情况呢,大家