Jsf 焊接和#x2B;GF4和x2B;会话处理:有时是错误的bean?
TL;DR我们得到了Jsf 焊接和#x2B;GF4和x2B;会话处理:有时是错误的bean?,jsf,session,glassfish,cdi,weld,Jsf,Session,Glassfish,Cdi,Weld,TL;DR我们得到了@SessionScopedbean实例,这些实例注入了另一个会话的内容 最近,我们的两个客户系统出现了严重问题。我们的客户正在两台机器上运行同一个JSF2.2应用程序的两个独立实例,这两台机器上有一台Glassfish 4.0服务器和WELD 2.0.5(真是内存泄漏!) 一些用户报告问题,例如提交表单后,响应显示的用户名与最初登录的用户名不同。由于我们无法在开发和测试环境中重现这种行为,我们开始从生产系统中获取日志数据 我们在记录什么? 在第一次尝试时,我们开始记录某个
@SessionScoped
bean实例,这些实例注入了另一个会话的内容
最近,我们的两个客户系统出现了严重问题。我们的客户正在两台机器上运行同一个JSF2.2应用程序的两个独立实例,这两台机器上有一台Glassfish 4.0服务器和WELD 2.0.5(真是内存泄漏!) 一些用户报告问题,例如提交表单后,响应显示的用户名与最初登录的用户名不同。由于我们无法在开发和测试环境中重现这种行为,我们开始从生产系统中获取日志数据 我们在记录什么? 在第一次尝试时,我们开始记录某个用户在某个时间从哪个客户端执行了哪个操作。在对日志进行爬网后,我们发现了如下序列:
Time Client User Action
.............................
t=0 ClientA UserA Login
t=1 ClientA UserA Logoff
t=2 ClientB UserB Login
t=3 ClientB UserB ActionA
t=4 ClientB *UserA* ActionB
t=5 ClientB UserB Logoff
而替换用户(此处为用户A
)的会话在替换发生之前并不总是结束(有时会导致一个用户注销另一个用户…)。那么,当前登录的用户存储在哪里?我们将其作为属性存储在@SessionScoped
bean中,该bean被注入到@RequestScoped
bean中,只要我们需要这些信息。这让我们得出了这样的理论:@sessionscope
bean有时会混淆
@Named
@javax.enterprise.context.SessionScoped
public class SessionStateBean {
private User user;
public void setUser(...) { }
public User getUser() { }
}
因此,在第二次尝试中,我们通过以下功能扩展了日志数据:
- 我们开始将用户名存储在HTTP会话中,并在每个请求中将其与来自
bean的值进行比较@SessionScoped
bean的每个实例在构建和销毁bean以及更改用户属性时都会收到自己的UUID并记录。我们知道,@SessionScoped
bean可能有多个代理,可以被钝化,等等,但是我们尝试了一下@SessionScoped
Time Session Client User Action
.............................
t=0 SessA ClientA UserA Login
t=1 SessA ClientA UserA Logoff
t=2 SessB ClientB UserB Login
t=3 SessB ClientB UserB ActionA
t=4 |SessB ClientB *UserA* ActionB
+-> SessionScope != Session
t=5 SessB ClientB UserB Logoff
考虑到正在处理的所有请求,会话范围值与会话值不匹配的请求约为60到150个请求中的1个
更有趣的是@SessionScoped
bean实例发生了什么。由于我们正在跟踪@PostConstruct
和@PreDestroy
事件,观察到如下序列:
Time Session Bean Action UserValue
................................
t=0 SessA BeanA Construct (null)
t=1 SessA BeanA SetUser UserA // login
t=2 SessA BeanA SetUser (null) // logout
t=3 SessA BeanA Destroy (null)
// so far so good, now it gets interesting
t=4 SessB BeanA SetUser UserB // login
t=5 SessB BeanA SetUser (null) // logout
t=6 SessC BeanA SetUser UserC // login
t=7 SessC BeanA SetUser (null) // logout
t=8 SessD BeanA SetUser UserD // login
t=9 SessD BeanA SetUser (null) // logout
我们没有料到,有时在@PreDestroy
事件bean实例被重用之后,却没有经过构建和销毁的生命周期。考虑到所有记录的bean实例,在500个(系统A)到4000个(系统B)中大约有1个bean会发生这种情况。当会话范围值和HTTP会话值不同时,这种情况并不总是发生,但当我们看到这样一个bean实例被重用时,它总是在值不同时发生
最初,我们假设这些事件更有可能在服务器负载不足一段时间后发生,但事实证明并非如此。有时,它们发生在最后一次服务器重新启动数小时后,有时发生在两周后
在互联网上搜索这些问题,我们无法找到其他人在WELD()、Glassfish()、Grizzly()、JSF等方面遇到相同问题或已知错误的实际痕迹
所以我们的问题是:有没有人经历过类似的问题?不知何故,这种奇怪的行为是我们刚刚试图在错误的位置识别的已知错误吗?有没有真正的解决办法?任何暗示都将不胜感激
更新:我们发现,如果重新启动整个机器,所描述的行为将消失大约两周。如果我们只是重新启动Glassfish,那么这种奇怪的行为将在数小时内恢复。说真的,什么会严重影响Glassfish或JVM,以至于只有机器重新启动才会改变行为
目前,我们通过将保存在
@SessionScoped
bean中的所有数据直接放入HTTP会话来绕过这个问题,到目前为止,它似乎工作正常。但是这种方法太笨拙了…而不是以这种方式注入会话范围的bean:
@Inject
private SessionBean sessionBean;
@Inject
private Instance<SessionBean> sessionBean;
尝试以这种方式注入:
@Inject
private SessionBean sessionBean;
@Inject
private Instance<SessionBean> sessionBean;
@Inject
私有实例sessionBean;
对于每次出现的僵尸会话,您能否确认正在使用新的JSESSION_ID,而不是重复使用旧的JSESSION_ID?使用JSF的@sessionScope
是您的一个选项吗?是的,我可以确认会话ID不是旧会话ID的重复。我在上面的列表中添加了会话ID信息。JSF的@SessionScope
对我们来说不是一个选项,因为它不能与所有其他CDI注释很好地结合在一起。我看到当CDI和JSF注释混合在一起时会发生一些非常奇怪的事情。您能否确认您的@SessionScoped
bean确实是用@javax.enterprise.context.SessionScoped
注释的,而不是@javax.faces.bean.SessionScoped
?是的,我还可以确认正在使用的注释是javax.enterprise.context.SessionScoped
。我刚才还注意到,bean没有显式的serialVersionUID
。这对@SessionScoped
bean很重要吗?