xiaobaoqiu Blog

Think More, Code Less

Memcached存储枚举

今天发布线上碰到一个小问题,Memcached存储的数据类型包括枚举的时候,当修改枚举的包路径之后,会出现问题。 异常信息如下,比较直观,就是数据的反序列化失败:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
[2015-11-23 18:04:45 ERROR net.rubyeye.xmemcached.transcoders.BaseSerializingTranscoder:113] Caught CNFE decoding 944 bytes of data
java.lang.ClassNotFoundException: Xxx.Xxx.Xxx.Xxx.RoomStatus
        at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1702) ~[catalina.jar:7.0.47]
        at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1547) ~[catalina.jar:7.0.47]
        at net.rubyeye.xmemcached.transcoders.BaseSerializingTranscoder$1.resolveClass(BaseSerializingTranscoder.java:102) ~[xmemcached-1.4.1.jar:na]
        at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1612) ~[na:1.7.0_45]
        at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1517) ~[na:1.7.0_45]
        at java.io.ObjectInputStream.readEnum(ObjectInputStream.java:1725) ~[na:1.7.0_45]
        at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1347) ~[na:1.7.0_45]
        at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:1990) ~[na:1.7.0_45]
        at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1915) ~[na:1.7.0_45]
        at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1798) ~[na:1.7.0_45]
        at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1350) ~[na:1.7.0_45]
        at java.io.ObjectInputStream.readObject(ObjectInputStream.java:370) ~[na:1.7.0_45]
        at java.util.ArrayList.readObject(ArrayList.java:771) ~[na:1.7.0_45]
        at sun.reflect.GeneratedMethodAccessor41.invoke(Unknown Source) ~[na:na]
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.7.0_45]
        at java.lang.reflect.Method.invoke(Method.java:606) ~[na:1.7.0_45]
        at java.io.ObjectStreamClass.invokeReadObject(ObjectStreamClass.java:1017) ~[na:1.7.0_45]
        at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1893) ~[na:1.7.0_45]
        at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1798) ~[na:1.7.0_45]
        at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1350) ~[na:1.7.0_45]
        at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:1990) ~[na:1.7.0_45]
        at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1915) ~[na:1.7.0_45]
        at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1798) ~[na:1.7.0_45]
        at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1350) ~[na:1.7.0_45]
        at java.io.ObjectInputStream.readObject(ObjectInputStream.java:370) ~[na:1.7.0_45]
        at net.rubyeye.xmemcached.transcoders.BaseSerializingTranscoder.deserialize(BaseSerializingTranscoder.java:106) ~[xmemcached-1.4.1.jar:na]
        at net.rubyeye.xmemcached.transcoders.SerializingTranscoder.decode0(SerializingTranscoder.java:92) [xmemcached-1.4.1.jar:na]
        at net.rubyeye.xmemcached.transcoders.SerializingTranscoder.decode(SerializingTranscoder.java:86) [xmemcached-1.4.1.jar:na]
        at net.rubyeye.xmemcached.XMemcachedClient.fetch0(XMemcachedClient.java:630) [xmemcached-1.4.1.jar:na]
        at net.rubyeye.xmemcached.XMemcachedClient.get0(XMemcachedClient.java:1030) [xmemcached-1.4.1.jar:na]
        at net.rubyeye.xmemcached.XMemcachedClient.get(XMemcachedClient.java:988) [xmemcached-1.4.1.jar:na]
        at net.rubyeye.xmemcached.XMemcachedClient.get(XMemcachedClient.java:999) [xmemcached-1.4.1.jar:na]

场景如下:

  1. Memcached已经有数据,数据中包含RoomStatus数据;
  2. 修改了枚举的包路径,发布线上;
  3. 从Cache中读取之前的数据会出现枚举反序列化异常;

在本地代码中重现了一下问题,并看了一下使用枚举的情况下其他可能处心问题的场景,结论如下(前提是Cache中已经存在使用RoomStatus数据):

  1. 修改包路径,会导致get数据反序列化失败,java.lang.ClassNotFoundException: Xxx.Xxx.Xxx.Xxx.RoomStatus;
  2. RoomStatus增加项,正常;
  3. RoomStatus减少项(已Cache数据中未使用到的项),正常;
  4. RoomStatus减少项(已Cache数据中使用到的项),异常:enum constant Xxx does not exist in Xxx.Xxx.Xxx.Xxx.RoomStatus;

综上,在Memcached中cache包含枚举的数据时候,建议如下:

  1. 不要修改枚举的包路径;
  2. 不要删除枚举中的数据;

这些序列化和反序列化的逻辑再XMemcached客户端实现的。详见SerializingTranscoder类,包含数据的encode和decode逻辑。再使用Memcached的时候也可以使用