日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

一次线上Redis类转换异常排查引发的思考

發(fā)布時間:2024/4/15 数据库 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 一次线上Redis类转换异常排查引发的思考 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

之前同事反饋說線上遇到Redis反序列化異常問題,異常如下:

XxxClass1 cannot be cast to XxxClass2

已知信息如下:

  • 該異常不是必現(xiàn)的,偶爾才會出現(xiàn);
  • 出現(xiàn)該異常后重啟應(yīng)用或者過一會就好了;
  • 序列化協(xié)議使用了hessian。

因為偶爾出現(xiàn),首先看了報異常那塊業(yè)務(wù)邏輯是不是有問題,看了一遍也發(fā)現(xiàn)什么問題。看了下對應(yīng)日志,發(fā)現(xiàn)是在Redis讀超時之后才出現(xiàn)的該異常,因此懷疑redis client操作邏輯那塊導(dǎo)致的(公司架構(gòu)組對redis做了一層封裝),發(fā)現(xiàn)獲取/釋放redis連接如下代碼:

1 try { 2 jedis = jedisPool.getResource(); 3 // jedis業(yè)務(wù)讀寫操作 4 } catch (Exception e) { 5 // 異常處理 6 } finally { 7 if (jedis != null) { 8 // 歸還給連接池 9 jedisPool.returnResourceObject(jedis); 10 } 11 }

初步認(rèn)定原因為:發(fā)生了讀寫超時的連接,直接歸還給連接池,下次使用該連接時讀取到了上一次Redis返回的數(shù)據(jù)。因此本地驗證下,示例代碼如下:

1 @Data 2 @NoArgsConstructor 3 @AllArgsConstructor 4 static class Person implements Serializable { 5 private String name; 6 private int age; 7 } 8 @Data 9 @NoArgsConstructor 10 @AllArgsConstructor 11 static class Dog implements Serializable { 12 private String name; 13 } 14 15 public static void main(String[] args) throws Exception { 16 JedisPoolConfig config = new JedisPoolConfig(); 17 config.setMaxTotal(1); 18 JedisPool jedisPool = new JedisPool(config, "192.168.193.133", 6379, 2000, "123456"); 19 20 Jedis jedis = jedisPool.getResource(); 21 jedis.set("key1".getBytes(), serialize(new Person("luoxn28", 26))); 22 jedis.set("key2".getBytes(), serialize(new Dog("tom"))); 23 jedisPool.returnResourceObject(jedis); 24 25 try { 26 jedis = jedisPool.getResource(); 27 Person person = deserialize(jedis.get("key1".getBytes()), Person.class); 28 System.out.println(person); 29 } catch (Exception e) { 30 // 發(fā)生了異常之后,未對該連接做任何處理 31 System.out.println(e.getMessage()); 32 } finally { 33 if (jedis != null) { 34 jedisPool.returnResourceObject(jedis); 35 } 36 } 37 38 try { 39 jedis = jedisPool.getResource(); 40 Dog dog = deserialize(jedis.get("key2".getBytes()), Dog.class); 41 System.out.println(dog); 42 } catch (Exception e) { 43 System.out.println(e.getMessage()); 44 } finally { 45 if (jedis != null) { 46 jedisPool.returnResourceObject(jedis); 47 } 48 } 49 }

連接超時時間設(shè)置2000ms,為了方便測試,可以在redis服務(wù)器上使用gdb命令斷住redis進(jìn)程(如果redis部署在Linux系統(tǒng)上的話,還可以使用iptable命令在防火墻禁止某個回包),比如在執(zhí)行?jedis.get("key1".getBytes()?代碼前,對redis進(jìn)程使用gdb命令斷住,那么就會導(dǎo)致讀取超時,然后就會觸發(fā)如下異常:

Person cannot be cast to Dog

既然已經(jīng)知道了該問題原因并且本地復(fù)現(xiàn)了該問題,對應(yīng)解決方案是,在發(fā)生異常時歸還給連接池時關(guān)閉該連接即可(jedis.close內(nèi)部已經(jīng)做了判斷),代碼如下:

1 try { 2 jedis = jedisPool.getResource(); 3 // jedis業(yè)務(wù)讀寫操作 4 } catch (Exception e) { 5 // 異常處理 6 } finally { 7 if (jedis != null) { 8 // 歸還給連接池 9 jedis.close(); 10 } 11 }

至此,該問題解決。注意,因為使用了hessian序列化(其包含了類型信息,類似的有Java本身序列化機(jī)制),所有會報類轉(zhuǎn)換異常;如果使用了json序列化(其只包含對象屬性信息),反序列化時不會報異常,只不過因為不同類的屬性不同,會導(dǎo)致反序列化后的對象屬性為空或者屬性值混亂,使用時會導(dǎo)致問題,并且這種問題因為沒有報異常所以更不容易發(fā)現(xiàn)。

?

既然說到了Redis的連接,要知道的是,Redis基于RESP(Redis Serialization Protocol)協(xié)議來通信,并且通信方式是停等方式,也就說一次通信獨占一個連接直到client讀取到返回結(jié)果之后才能釋放該連接讓其他線程使用。小伙伴們可以思考一下,Redis通信能否像dubbo那樣使用單連接+序列號(標(biāo)識單次通信)通信方式呢?理論上是可以的,不過由于RESP協(xié)議中并沒有一個"序列號"的字段,所以直接靠原生的通信方法來實現(xiàn)是不現(xiàn)實的。不過我們可以通過echo命令傳遞并返回"序列號"+正常的讀寫方式來實現(xiàn),這里要保證二者執(zhí)行的原子性,可以通過lua腳本或者事務(wù)來實現(xiàn),事務(wù)方式如下:

MULTI ECHO "唯一序列號" GET key1 EXEC

然后客戶端收到的結(jié)果是一個?[ "唯一序列號", "value1" ]的列表,你可以根據(jù)前一項識別出這是你發(fā)送的哪個請求。

為什么Redis通信方式并沒有采用類似于dubbo這種通信方式呢,個人認(rèn)為有以下幾點:

  • 使用停等這種通信方式實現(xiàn)簡單,并且協(xié)議字段盡可能緊湊;
  • Redis都是內(nèi)存操作,處理性能較強,停等協(xié)議不會造成客戶端等待時間較長;
  • 目前來看,通信方式這塊不是Redis使用上的性能瓶頸,這一點很重要。

?

推薦閱讀:

  • 別再問我ConcurrentHashMap了
  • 分布式鎖設(shè)計與實現(xiàn)

  • ConcurrentHashMap竟然也有死循環(huán)問題?

  • 你的ThreadLocal線程安全么

?歡迎小伙伴掃描以下二維碼精彩好文。

?

轉(zhuǎn)載于:https://www.cnblogs.com/luoxn28/p/11075958.html

總結(jié)

以上是生活随笔為你收集整理的一次线上Redis类转换异常排查引发的思考的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。