在一个Spring boot项目中,需要使用redis作为缓存,于是将使用spring-boot-starter-data-redis,具体依赖如下:
(相关资料图)
org.springframework.boot spring-boot-starter-data-redis 2.0.4.RELEASE
在测试环境中,功能测试,压力测试,都没有发现问题,原因是测试环境中redis自行搭建,没有设置密码,但是上线后,Redis使用的是A***的Pass服务的集群,并设置密码,使用过程中发现如下问题:
redis负载高;redis异常,错误信息: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
原本打算看一下是否是代码逻辑问题导致redis负载过高,于是登录redis服务器使用monitor命令观察命令执行的频率,发现每执行一次命令都执行一次Auth password
命令,说明连接池未正确使用导致执行一次命令创建一次连接,导致负载高 ,并且代码执行效率低 。
然后对比了使用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
网上搜索了一下,发现项目github上已有此问题的反馈以及解决办法github.com/lettuce-io/…,原因是由于Lettuce其中有个配置项validateClusterNodeMembership
默认是true导致;
由于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; }}
吃一堑、长一智,总结如下:
开发+测试环境尽量与线上一致,可提前发现问题;使用新技术需要多多测试再投入生产使用;标签:
精彩推荐
金融界12月30日消息据交通运输部,30日,交通运输新业态协同监管部际联席会议办公室对滴滴出行、T3出行...
12月29日,沪指报收3073 7,较前一交易日下跌0 44%。特斯拉板块整体表现一般,较前一交易日下跌0 27%...
昨天大盘最后一小时放量上涨,外围股市普遍上涨,消息面偏向平静。今天大盘应该高开,上攻时关注3086点...
作文给同学的礼物第一篇有一次,我参加了“阅读交流”会,当小老师。ppt已经做完了,就差给同学们分的小...
格隆汇12月29日丨科伦药业公布,四川科伦博泰生物医药股份有限公司(简称“科伦博泰”)是公司的控股子公...
格隆汇12月29日丨紫元元公告,于2022年12月29日,卖方叶特与买方深圳市美佳尔健康管理有限公司(公司间接...
开尔新材(300234)12月29日在投资者关系平台上答复了投资者关心的问题。投资者:董秘您好,公司卖掉旧厂...
网贷逾期一般会上征信,有些借贷机构在用户逾期后一天后就会上报给征信机构,而有些借贷机构则是会在几天...
东吴证券国际经纪有限公司陈睿彬近期对比亚迪进行研究并发布了研究报告《2022年三季报点评:业绩符合市...
中新社北京12月28日电(记者阮煜琳)中国生态环境部近日发布了《企业温室气体排放核算与报告指南发电设施...
作为一个设计行业的社畜,除了发际线往后走,还有那稀疏几乎不见头发的三角区域…一直是我的痛!要想富...
《证券日报》记者经过梳理发现,2022年商业银行二级资本债发行的井喷,与发债银行数量大幅增加有着直接关系
海口第二批发热就医服务站海口市于12月25日开设了8个“一站式”发热就医服务站,有效缓解了医疗机构的接...
1、车辆行驶证原件或复印件;2、车辆驾驶人驾驶证原件;3、车辆驾驶人身份证原件;4、现金和银行卡。交通事...
合肥邮政储蓄银行2023贺岁纪念币预约时间+兑换时间:(一)预约期:2022年12月27日22:30-12月29日24:00...
珠海横琴新区抗原检测试剂零售药店区域供应商连锁门店名称地址联系电话横琴新区大参林医药集团股份有限...
同花顺数据中心显示,国安达12月26日获融资买入81 71万元,占当日买入金额的7 22%,当前融资余额1 33...
中证网讯(记者武卫红)12月26日晚,海航控股披露定增结果公告,海航控股股东方大集团通过瀚巍投资向上...
编号:DSH-06-77 时代出版会议文件 日期:2022...
2023年的春晚将会在一个多月后开始,可是节目组已经基本确认了节目表演的节目和名单。
资讯News
03-19
03-19
03-19
03-19
03-19
03-18
03-18
03-18
03-18
03-18
03-18
03-18
03-17
03-17
03-17
03-17
03-17
03-17
03-17
03-17
聚焦Policy
当好农民工的“护薪人” 近日,罗某等7名农民工在收到被拖欠的工资后,纷纷打电话向江西省南昌市...
“通讯录里所有人都知道我欠钱了” □ 本报记者 韩丹东 □ 本报见习记者 张守坤 ...
大连宝马车撞人案肇事司机被判死刑 本报讯 记者韩宇 10月29日,辽宁省大连市中级人民法院一审...
医院财务迷上网络赌博输光5000万元公款 □ 本报记者 马维博 □ 本报通讯员 汪宇堂 曹...
辊环车削 雕琢毫厘(工匠绝活) 【绝活看点】 23年来,雷虎始终扎根一线,改进钢材轧制工艺...
交警严查超标电动自行车挪用“白牌” 截至昨晚6时,处罚电动自行车违法行为共计6585笔;下一步将...
明起寒潮来袭 北方气温普降10℃以上 中央气象台预计,本周日北京平原地区最低气温降至-4℃左右...
多种蔬菜价格降幅达五成 包括菠菜、蒿子秆等 预计本月中旬蔬菜恢复供需平衡 本报讯(记者...
北京周日最低气温或达-4℃ 本报讯(记者 赵婷婷)北京青年报记者昨天从中央气象台获悉,新一股...
昌平一家四口确诊新冠肺炎 天通北苑第二社区升级为中风险地区 朝阳两涉疫校区及16所学校停课 ...
图集Picture