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

mar*_*383 7 session jsf 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
Run Code Online (Sandbox Code Playgroud)

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

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

  public void setUser(...) { }
  public User getUser() { }
}
Run Code Online (Sandbox Code Playgroud)

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

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

关于第一个日志功能,我们开始看到序列显示来自会话范围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
Run Code Online (Sandbox Code Playgroud)

考虑到正在处理的所有请求,会话范围值与会话值不匹配的请求大约为.60到150个请求中的1个.

更有趣的是@SessionScopedbean实例发生了什么.由于我们正在跟踪@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
Run Code Online (Sandbox Code Playgroud)

我们没想到有时在@PreDestroy事件bean实例被重用之后却没有经过构建和破坏的生命周期.将所有记录的bean实例考虑在内,这种情况大约发生.从500(系统A)到4000(系统B)中的1个bean.当会话范围值和HTTP会话值不同时,这并不总是发生,但是当我们看到这样的bean实例被重用时,它总是在值不同时发生.

最初我们假设这些事件更有可能在服务器负载一段时间后发生,但事实证明并非如此.有时它们在最后一次服务器重启后几个小时发生,有时在两周后发生.

在互联网上搜索这些问题,我们无法找到遇到相同问题的其他人的实际痕迹或WELD(最佳痕迹,但错误版本),Glassfish,Grizzly(最佳痕迹,但太旧),JSF,等等

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

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


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

小智 0

不要以这种方式注入会话作用域 bean:

@Inject
private SessionBean sessionBean;
Run Code Online (Sandbox Code Playgroud)

尝试这样注入:

@Inject
private Instance<SessionBean> sessionBean;
Run Code Online (Sandbox Code Playgroud)