<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
最近工作中遇到了這樣一個場景
同一個外部單號生成了多張出庫單,等待所有相關的出庫單都出庫成功後回覆成功訊息外部系統呼叫方。因為是分散式布系統,我使用了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是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 沒有專用的整數型別,所以 key 內儲存的字串被解釋為十進位制 64 位有符號整數來執行 INCR 操作。如果值包含錯誤的型別,或字串型別的值不能表示為數位,那麼返回一個錯誤。
從redis範例中檢視該key的value,會發現結果類似這樣:
"xacxedx00x05srx00x11java.lang.Integerx12xe2xa0xa4xf7x81x878x02x00x01Ix00x05valuexrx00x10java.lang.Numberx86xacx95x1dx0bx94xe0x8bx02x00x00xpx00x00x00x01"
原因在於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,嘗試之後,完美解決。
以上為個人經驗,希望能給大家一個參考,也希望大家多多支援it145.com。
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45