业务老师反馈某个系统的用户添加功能突然不能用了(系统近期没有上线改动),服务器看了下日志发现是空指针问题,心想,空指针问题,那可太简单了
示意代码如下:
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
public class UserDTO implements Externalizable {
private String id;
private String username;
private String email;
private String role;
private String organ;
public UserDTO() {}
public UserDTO(String id, String username, String email, String role, String organ) {
this.id = id;
this.username = username;
this.email = email;
this.role = role;
this.organ = organ;
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeUTF(id);
out.writeUTF(username);
out.writeUTF(email);
out.writeUTF(role);
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
this.id = in.readUTF();
this.username = in.readUTF();
this.email = in.readUTF();
this.role = in.readUTF();
}
@Override
public String toString() {
return "UserDTO{" +
"id='" + id + '\'' +
", username='" + username + '\'' +
", email='" + email + '\'' +
", role='" + role + '\'' +
", organ='" + organ + '\'' +
'}';
}
}
import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Element;
public class LoginService {
private static final CacheManager cacheManager = CacheManager.newInstance("src/main/resources/ehcache.xml");
private static final Cache loginUserCache = cacheManager.getCache("loginUserCache");
// 模拟登录
public static void login(String userId, String username, String email, String role) {
UserDTO user = new UserDTO(userId, username, email, role);
// 保存到 Ehcache
loginUserCache.put(new Element(userId, user));
System.out.println("用户已登录并写入缓存: " + user);
}
// 从缓存中获取用户
public static UserDTO getUser(String userId) {
Element element = loginUserCache.get(userId);
if (element != null) {
return (UserDTO) element.getObjectValue();
}
return null;
}
}
如上代码所示,一般登陆之后,会将用户信息保存到缓存中,下次访问的时候,会从缓存中获取用户信息。某个功能发现从缓存里面取出来的user是有的, 但是机构字段却是空的,到处翻遍了代码发现,无论哪里都set了机构值,不可能为空(一整天都在到处翻代码思考到底是哪里漏set了,代码中还有一些ThreadLocal的东西,一度怀疑是不是多线程并发导致的问题)。
<!--name:缓存名称-->
<!--maxElementsInMemory:缓存最大个数-->
<!--eternal:缓存中对象是否为永久的,如果是,超时设置将被忽略,对象从不过期-->
<!--timeToIdleSeconds:置对象在失效前的允许闲置时间(单位:秒),仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大-->
<!--timeToLiveSeconds:缓存数据的生存时间(TTL),也就是一个元素从构建到消亡的最大时间间隔值,这只能在元素不是永久驻留时有效,如果该值是0就意味着元素可以停顿无穷长的时间-->
<!--overflowToDisk:内存不足时,是否启用磁盘缓存-->
<!--maxElementsOnDisk:设置成0 表示硬盘中最大缓存对象数无限大-->
<!--diskPersistent:设置成true表示缓存虚拟机重启期数据磁盘存储是否在虚拟机重启后持续存在-->
<cache name="loginUserCache"
maxElementsInMemory="5000"
eternal="true"
maxElementsOnDisk="10000000"
overflowToDisk="true"
diskPersistent="true"
memoryStoreEvictionPolicy="LRU">
<!--<persistence strategy="localRestartable" synchronousWrites="false"/>-->
</cache>
回家路上突然灵感爆发,怀疑是ehcache序列化问题,果然一查发现maxElementsInMemory配置的5000,也就是当登陆超过5000次之后, 缓存就会开始写磁盘,而USERDTO的readExternal和writeExternal方法又漏了organ字段,序列化的时候就丢失了organ字段值。最终表现出来的现象是,登陆超过5000次之后,某些需要获取用户机构的功能就失效了。