当前位置: 首页 > 知识库问答 >
问题:

焊接GF4会话范围:有时是错误的bean?

景嘉志
2023-03-14

TL;DR我们得到@SessionScopedbean实例,这些实例注入了另一个会话的内容

最近,我们的两个客户系统出现了严重问题。我们的客户正在两台机器上运行同一个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)的会话并不总是在替换发生之前结束(有时会导致一个用户注销另一个用户...)。那么当前登录的用户存储在哪里?我们将其作为属性存储在@SessionScopedbean中,并将其注入到需要此信息的任何地方。这使我们得出这样的理论,即@SessionScopedbean有时会混淆。

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

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

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

  • 我们开始将用户名存储在HTTP会话中,并在每个请求中将其与来自@SessionScopedbean的值进行比较
  • @SessionScopedbean的每个实例都收到自己的UUID,并在构建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个。

更有趣的是@SessionScopedbean实例发生了什么。因为我们正在跟踪@PostConstruct

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或JVM,以至于只有机器重新启动才能改变行为?

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

共有1个答案

栾峰
2023-03-14

而不是这样注入会话作用域bean

@Inject
private SessionBean sessionBean;

尝试以这种方式注入:

@Inject
private Instance<SessionBean> sessionBean;
 类似资料:
  • 在我们生产中的一个系统中,我们在jboss 8.2和最新的JDK 7、centos 7 64位、javax.enterprise.context.SessionScoped bean上的最新primeface中遇到了一个非常奇怪的问题。(整个项目中不使用jsf注释,仅使用CDI注释以避免潜在冲突) 在处理一个请求的过程中,@SessionScoped bean在某个时间点(我们不知道是什么触发了它

  • HttpSession 对象必须被限定在应用(或 servlet 上下文)级别。底层的机制,如使用 cookie 建立会话,不同的上下文可以是相同,但所引用的对象,包括该对象中的属性,决不能在容器上下文之间共享。 用一个例子来说明该要求: 如果 servlet 使用 RequestDispatcher 来调用另一个 Web 应用的 servlet,任何创建的会话和被调用 servlet 所见的必须

  • 我正试图将一个Tomcat应用程序移植到JettyRunner9.2。我想将我的应用程序移到Heroku,所以我需要用嵌入式服务器或JettyRunner启动它。我认为JettyRunner将是最容易的,因为我可以保持一个战争格式,并使它容易港口离开,如果需要。 任何帮助都将不胜感激。如果我不能让它很快工作,我可以尝试嵌入式Tomcat,或者考虑不需要更改容器的托管。 如果使用Jetty的不同方法

  • 我编写了一个会话范围的托管bean,用于在domino xpages应用程序中缓存sesion用户特定的信息,如下代码所示: 然后,我在faces配置中声明了它,使其成为会话范围的bean: 在测试bean时,我通过#{NBUser.fullUserName}使用它,它并不总是返回当前domino http会话上下文的用户,例如,我首先由用户“user1”登录并注销,然后由user2登录,bean

  • 我们知道Spring框架提供了单例、原型、请求、会话、全局会话bean范围。 我们还知道Spring Web流提供了flow Scope、viewScope、Request estScope、flash Scope、versationScope。 因此,如果我在spring MVC项目中提到一个组件,比如说Student,作为@Component@Scope=singleton。对于每个请求,它会

  • 在用户登录时续订HTTP会话是常见的最佳做法。这将强制使用新的会话ID,从而避免会话固定漏洞。 当涉及@SessionScoped bean时,是否有使用CDI实现此功能的首选模式?困难在于,通过使当前HTTP会话无效,您将在下一个请求中获得不同的会话范围bean,但直到下一个请求。 例如,假设一个用于存储用户登录信息的会话bean: 和另一个用于管理登录的bean: 对于托管Bean,这将检索一