TL;DR我们得到@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)的会话并不总是在替换发生之前结束(有时会导致一个用户注销另一个用户...)。那么当前登录的用户存储在哪里?我们将其作为属性存储在@SessionScopedbean中,并将其注入到需要此信息的任何地方。这使我们得出这样的理论,即@SessionScoped
bean有时会混淆。
@Named
@javax.enterprise.context.SessionScoped
public class SessionStateBean {
private User user;
public void setUser(...) { }
public User getUser() { }
}
因此,在第二次尝试中,我们通过以下功能扩展了日志数据:
@SessionScoped
bean的值进行比较
@SessionScoped
bean的每个实例都收到自己的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个。
更有趣的是@SessionScoped
bean实例发生了什么。因为我们正在跟踪@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,以至于只有机器重新启动才能改变行为?
目前,我们通过将保存在@SessionScoped
bean中的所有数据直接放入HTTP会话来绕过这个问题,到目前为止,它似乎工作正常。但这种方法太笨拙了。。。
而不是这样注入会话作用域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,这将检索一