WELD + GF4 + SessionScoped:有时错误的bean?

时间:2014-07-29 14:04:04

标签: jsf session glassfish cdi weld


TL; DR 我们注入的@SessionScoped bean实例具有另一个会话的内容


最近我们遇到了两个客户系统的严重问题。我们的客户在具有WELD 2.0.5的Glassfish 4.0服务器的两台机器上运行相同JSF 2.2应用程序的两个独立实例(因内存泄漏而欢呼!)。

有些用户报告了以下问题:提交表单时,响应显示的用户名与最初登录的用户名不同。由于我们无法在开发和测试环境中重现此行为,因此我们开始从生产系统中获取日志数据。

我们记录了什么?

在我们第一次尝试时,我们开始记录哪个用户在某个时间从哪个客户执行了哪个操作。爬过日志后,我们发现了如下序列:

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

替换用户的会话(此处User A)在更换发生之前并不总是结束(有时会导致一个用户注销另一个用户......)。那么当前登录的用户存储在哪里?我们将它作为一个属性存储在@SessionScoped bean中,只要我们需要这些信息,就会注入@RequestScoped bean。这导致我们认为@SessionScoped豆子有时混淆了。

@Named
@javax.enterprise.context.SessionScoped
public class SessionStateBean {
  private User user;

  public void setUser(...) { }
  public User getUser() { }
}

因此,在第二次尝试时,我们通过以下功能扩展了日志数据:

  • 我们开始在HTTP会话中存储用户名,并在每个请求中将其与来自@SessionScoped bean的值进行比较。
  • @SessionScoped bean的每个实例都接收到自己的UUID,并在构建bean时记录。销毁以及用户属性更改时。我们知道@SessionScoped bean可能有多个代理,被钝化等等,但我们试了一下。

关于第一个日志功能,我们开始看到序列显示来自会话范围bean的用户名与存储在HTTP会话中的值之间的实际差异:

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(best trace, but wrong version),Glassfish,Grizzly(best trace, but too old),JSF中找到遇到相同问题或已知错误的其他人的实际痕迹等等。

所以我们的问题是:有没有人遇到类似的问题?这个奇怪的行为是不是一个已知的错误,我们只是试图在错误的位置识别?有没有实际修复?很高兴有任何提示!

更新: 我们发现,如果我们重启整台机器,所描述的行为将会持续两周左右。如果我们只是重启Glassfish,那么奇怪的行为又回来了几个小时。严重的是,什么可能会严重影响Glassfish或JVM,只有机器重新启动会改变行为?


目前我们通过将我们保存在@SessionScoped bean中的所有数据直接放入HTTP会话来绕过这个问题,到目前为止似乎工作正常。但那种做法太笨拙......

1 个答案:

答案 0 :(得分:0)

而不是以这种方式注入会话范围的bean:

@Inject
private SessionBean sessionBean;

尝试以这种方式注入:

@Inject
private Instance<SessionBean> sessionBean;