我的编程空间,编程开发者的网络收藏夹
学习永远不晚

使用RedisAtomicInteger计数出现少计问题及解决

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

使用RedisAtomicInteger计数出现少计问题及解决

RedisAtomicInteger计数出现少计

最近工作中遇到了这样一个场景

同一个外部单号生成了多张出库单,等待所有相关的出库单都出库成功后回复成功消息外部系统调用方。因为是分布式布系统,我使用了RedisAtomicInteger计数器来判断出库单是否全部完成,数量达成时回复成功消息给外部系统调用方。

在本地测试和测试环境测试时都没有发现问题,到了生产环境后,发现偶尔出现所有出库单都已经出库,但没有回复消息给调用方,如:出库单15张,但计数器只有14。

分析

开始以为是有单据漏计算了,通过日志分析,发现所有的出库单都统计进去了。

然后通过增加打开调试日志,发现最开始的2张出库单统计后的值都为1,少了1个。

原因

redis的increment是原子性,但new RedisAtomicInteger时会调用set方法来设置初始值,set方法是可以被后面的方法覆盖的。

edisAtomicInteger redisAtomicInt = new RedisAtomicInteger(countKey, redisTemplate.getConnectionFactory());

// spring-data-redis-1.8.13原码
public RedisAtomicInteger(String redisCounter, RedisConnectionFactory factory) {
  this(redisCounter, factory, null);
 }

private RedisAtomicInteger(String redisCounter, RedisConnectionFactory factory, Integer initialValue) {
  RedisTemplate<String, Integer> redisTemplate = new RedisTemplate<String, Integer>();
  redisTemplate.setKeySerializer(new StringRedisSerializer());
  redisTemplate.setValueSerializer(new GenericToStringSerializer<Integer>(Integer.class));
  redisTemplate.setExposeConnection(true);
  redisTemplate.setConnectionFactory(factory);
  redisTemplate.afterPropertiesSet();

  this.key = redisCounter;
  this.generalOps = redisTemplate;
  this.operations = generalOps.opsForValue();

  if (initialValue == null) {
   if (this.operations.get(redisCounter) == null) {
    set(0);
   }
  } else {
   set(initialValue);
  }
 }

解决方法

网上看到的都是加业务锁或升级spring-data-redis版本。

但老项目升级spring-data-redis版本可能会引起兼容性问题,加业务锁又增加了代码复杂度。

那有没有更简单方法呢,有。竟然是set方法导致的值覆盖,那就不走set方法就可以了。

增加下面一行代码解决问题

// Fixed bug 前几个数累计重复问题
redisTemplate.opsForValue().setIfAbsent(countKey, 0);

使用RedisAtomicInteger中间遇到的问题

RedisAtomicInteger是springdata中在redis的基础上实现的原子计数器,在以下maven依赖包中:

<groupId>org.springframework.data</groupId>  
<artifactId>spring-data-redis</artifactId>

当使用RedisAtomicInteger(String redisCounter, RedisOperations<String, Integer> template,...)函数构建实例的情况下,在使用INCR或者DECR时,会遇到ERR value is not an integer or out of range错误,显示操作的数据不是一个整数或者超出范围。

参考redis命令说明我们知道incr对操作值的要求

这是一个针对字符串的操作,因为 Redis 没有专用的整数类型,所以 key 内储存的字符串被解释为十进制 64 位有符号整数来执行 INCR 操作。如果值包含错误的类型,或字符串类型的值不能表示为数字,那么返回一个错误。

从redis实例中查看该key的value,会发现结果类似这样:

"\xac\xed\x00\x05sr\x00\x11Java.lang.Integer\x12\xe2\xa0\xa4\xf7\x81x878\x02\x00\x01I\x00\x05valuexr\x00\x10java.lang.Number\x86\xac\x95\x1d\x0b\x94\xe0\x8b\x02\x00\x00xp\x00\x00\x00\x01"

原因在于value使用的序列化方式是JdkSerializationRedisSerializer,这和INCR命令对结果的要求是违背的。

该使用哪种序列化方式把value放进去呢?按照INCR命令对结果的要求,最容易想到StringRedisSerializer,但经过尝试这也行不通

会报java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String。

如果看过RedisAtomicInteger的源码,在private RedisAtomicInteger(String redisCounter, RedisConnectionFactory factory, Integer initialValue)中会发现方法内部创建了RedisTemplate实例,对value设置的序列化方式是GenericToStringSerializer。

该序列化内部使用spring core包下的
org.springframework.core.convert.support.DefaultConversionService作为默认的对象和字符串的转换方式,主要为了满足大多数环境的要求。

至此,我们终于知道了错误的根本原因,构造RedisAtomicInteger时传入的redisTemplate是有问题的,value的默认序列化方式不满足RedisAtomicInteger的需要。那么问题也迎刃而解,将GenericToStringSerializer作为redisTemplate的value序列化方式。

这样虽然解决了问题,但很麻烦,很可能为了RedisAtomicInteger的要求需要再创建一个redisTemplate,简直不能忍受。再看RedisAtomicInteger的源码,发现构造函数除了可以用redisTemplate,还可以用RedisConnectionFactory,尝试之后,完美解决。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

使用RedisAtomicInteger计数出现少计问题及解决

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

使用RedisAtomicInteger计数出现少计问题及解决

目录RedisAtomicInteger计数出现少计分析原因解决方法使用RedisAtomicInteger中间遇到的问题参考redis命令说明我们知道incr对操作值的要求RedisAtomicInteger计数出现少计最近工作中遇到了
2022-11-22

使用RedisAtomicInteger计数出现少计问题如何解决

这篇“使用RedisAtomicInteger计数出现少计问题如何解决”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“使用Re
2023-07-04

使用jBuilder8出现问题怎么解决

如果您在使用jBuilder8时遇到问题,可以尝试以下解决方案:确保您的操作系统和Java版本与jBuilder8的要求兼容。查看jBuilder8的官方文档或网站,了解最低要求和兼容性信息。检查您的jBuilder8安装是否正确。如果您是
使用jBuilder8出现问题怎么解决
2023-10-28

用Python解决计数原理问题的方法

前几天遇到这样一道数学题: 用四种不同颜色给三棱柱六个顶点涂色,要求每个点涂一种颜色,且每条棱的两个端点涂不同颜色,则不同的涂色方法有多少种?当我看完题目后,顿时不知所措。于是我拿起草稿纸在一旁漫无目的地演算了一下,企图能找到解决方法。结果
2022-06-04

vue3使用socket.io的出现的问题怎么解决

今天小编给大家分享一下vue3使用socket.io的出现的问题怎么解决的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。问题一
2023-07-05

怎么解决使用ip上网出现的问题

这篇文章主要讲解了“怎么解决使用ip上网出现的问题”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“怎么解决使用ip上网出现的问题”吧!访问某个网站时,如果网站页面无法打开,无法登录密码,抓取信
2023-06-20

使用绿色版SQLServer2008R2出现问题怎么解决

这篇文章主要讲解了“使用绿色版SQLServer2008R2出现问题怎么解决”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“使用绿色版SQLServer2008R2出现问题怎么解决”吧!1、绿
2023-07-05

windows计划任务使用方法附常见问题解决

计划任务应该是很老的一个系统的功能的!记javascript得从98下就有的! 也渐渐的被人淡忘了!android昨天从菜园某求助帖上发现还是有不少人对此不是很熟悉!另外网上的资料似乎也讲的不是很多!那就研究一下,做个教程大家熟悉一下好了!
2023-06-01

Windows XP系统下添加任务计划常出现问题解决办法

计划任务就是让电脑在指定的时间内执行指定的动作(计划动作),这些动作可以是一个程序,也可以是一个批处理,但是至少是可以运行的(通俗一些就是双击可以运行的)。 添加步骤:“开始”--&ghOdLVkuGTyt;&ldq
2023-06-02

解决使用redisTemplate set方法保存出现x00问题

在项目有个需求要保存一个字符串到redis,并设置一个过期时间。这个需求一看非常简单,使用redisTemplate一行代码搞定,代码如下redisTemplate.opsForValue().set("userKey", data, 10000); 但保存后
解决使用redisTemplate set方法保存出现x00问题
2016-03-30

springBoot中使用@Value取值出现问题如何解决

这篇文章主要介绍“springBoot中使用@Value取值出现问题如何解决”,在日常操作中,相信很多人在springBoot中使用@Value取值出现问题如何解决问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答
2023-07-02

法国服务器使用出现问题如何解决

解决法国服务器出现问题的方法取决于具体的问题类型。以下是一些常见的问题和解决方法:网络连接问题:检查服务器的网络连接是否正常,可以尝试重新启动服务器、调整网络设置或联系网络服务提供商解决问题。服务器崩溃或死机:尝试重新启动服务器,如果问题仍
法国服务器使用出现问题如何解决
2024-05-13

编程热搜

目录