亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

使用RedisAtomicInteger計數出現少計問題如何解決

發布時間:2022-11-23 10:37:50 來源:億速云 閱讀:124 作者:iii 欄目:開發技術

這篇“使用RedisAtomicInteger計數出現少計問題如何解決”文章的知識點大部分人都不太理解,所以小編給大家總結了以下內容,內容詳細,步驟清晰,具有一定的借鑒價值,希望大家閱讀完這篇文章能有所收獲,下面我們一起來看看這篇“使用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\x81\x878\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,嘗試之后,完美解決。

以上就是關于“使用RedisAtomicInteger計數出現少計問題如何解決”這篇文章的內容,相信大家都有了一定的了解,希望小編分享的內容對大家有幫助,若想了解更多相關的知識內容,請關注億速云行業資訊頻道。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

隆昌县| 福清市| 四平市| 上饶市| 秭归县| 永福县| 枣庄市| 康马县| 郧西县| 泰州市| 民丰县| 梧州市| 张家口市| 武山县| 武夷山市| 南开区| 长泰县| 麦盖提县| 石台县| 昌江| 买车| 霍林郭勒市| 宁夏| 宁德市| 灌云县| 汝南县| 龙里县| 灵川县| 荣昌县| 沐川县| 沾化县| 颍上县| 神池县| 鸡泽县| 福海县| 湘阴县| 柳林县| 和静县| 开平市| 宝坻区| 闽清县|