技术堆栈:
WebLogic 12.1.3
JSF 2.1
Primefaces 6.1
我正在寻求有关如何编码@SessionScoped ManagedBean以在所有集群服务器上进行复制的建议。
我的假设是,一旦SessionScoped,一个对象应该在所有集群服务器上复制,所有属性都是当前值。
在迁移到集群WebLogic平台时,我正在测试当前的JSF应用程序是否会在集群中的所有服务器上携带Session值。我的测试结果是失败了。然后我尝试编写一个简单的应用程序来监视ViewScoped和SessionScoped对象的行为方式。 WebLogic群集配置如下
ManagedServer_1具有IP x.x.x.212并侦听端口7003 ManagedServer_2具有IP x.x.x.212并侦听端口7004 我使用Fiddler来启用端口解析
我正在使用一个简单的单页JSP Web应用程序,我在后面的模型上更新了一个值。 Web应用程序 weblogic.xml中
<?xml version="1.0" encoding="UTF-8"?>
<weblogic-web-app xmlns="http://xmlns.oracle.com/weblogic/weblogic-web-app" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd http://xmlns.oracle.com/weblogic/weblogic-web-app http://xmlns.oracle.com/weblogic/weblogic-web-app/1.4/weblogic-web-app.xsd">
<jsp-descriptor>
<keepgenerated>true</keepgenerated>
<debug>true</debug>
</jsp-descriptor>
<context-root>/SessionTest</context-root>
<session-descriptor>
<persistent-store-type>replicated_if_clustered</persistent-store-type>
<debug-enabled>true</debug-enabled>
</session-descriptor>
</weblogic-web-app>
的web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<context-param>
<param-name>javax.faces.PROJECT_STAGE</param-name>
<param-value>Development</param-value>
</context-param>
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>/faces/*</url-pattern>
</servlet-mapping>
<session-config>
<session-timeout>
30
</session-timeout>
</session-config>
<welcome-file-list>
<welcome-file>faces/index.xhtml</welcome-file>
</welcome-file-list>
<distributable/>
</web-app>
的index.xhtml
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:p="http://primefaces.org/ui">
<h:head>
<title>Session Replication Test</title>
</h:head>
<h:body>
<h:form>
<p:growl id="grw" widgetVar="grw" showDetail="true"/>
<p:panel id="panel1" header="Ajax Panel @ ${controller.model.serverIP}:${controller.model.serverPort}">
<p:inputText id="name_input" value="${model.name}" widgetVar="name_in">
<p:ajax update="@this"/>
</p:inputText>
<br/><br/>
<p:commandButton id="name_command" value="Call Ajax" actionListener="${controller.updateNameLabel(model.name)}" update="@form"/>
<br/><br/>
<p:outputLabel id="name_output" value="Hello ${model.name} * ${model.clicks}"/>
</p:panel>
</h:form>
</h:body>
</html>
支持index.xhtml,我们有一个
模型类
@ManagedBean
@SessionScoped
public class Model implements Serializable {
private String name;
private int clicks;
private String serverIP;
private String serverPort;
public Model() {
name = "";
clicks = 0;
serverIP = "";
serverPort = "";
System.out.println("---------- Model constructor -----------");
}
// Emmanuel's suggestion applied here
private void pushSesssion(){
HttpSession httpSession = fetchSession();
httpSession.setAttribute("model", this);
}
private HttpSession fetchSession(){
ExternalContext ec = FacesContext.getCurrentInstance().getExternalContext();
HttpSession httpSession = (HttpSession) ec.getSession(false);
return httpSession;
}
… getters and setters
public void setClicks(int clicks) {
this.clicks = clicks;
pushSesssion();
}
// Emmanuel's suggestion - End
}
..和一个
控制器类
@ManagedBean
@ViewScoped
public class Controller implements Serializable {
@ManagedProperty(value = "#{model}")
private Model model;
/**
* Creates a new instance of Actions
*/
public Controller() {
}
@PostConstruct
public void init() {
System.out.println("----------------- Controller initializing @ " + new Date()+" ------------------------");
fetchIP();
}
private void fetchIP() {
ExternalContext ec = FacesContext.getCurrentInstance().getExternalContext();
HttpServletRequest request = (HttpServletRequest) ec.getRequest();
String localIP = request.getLocalAddr();
int localPort = request.getLocalPort();
if(!model.getServerIP().equals(localIP) || !model.getServerPort().equals(localPort+"")){
addFacesMessage(FacesMessage.SEVERITY_INFO,"Server Changed","Please wait 30 seconds before next click.");
model.setServerIP(localIP);
model.setServerPort(localPort+"");
}
}
private HttpSession fetchSession(){
ExternalContext ec = FacesContext.getCurrentInstance().getExternalContext();
HttpSession httpSession = (HttpSession) ec.getSession(false);
return httpSession;
}
public void updateNameLabel(String name) {
HttpSession httpSession = fetchSession();
System.out.println("DBG - "+new Date()+" HttpSession -- " + httpSession.getId());
System.out.println("---------------------- DBG 1 -------------------");
System.out.println("DBG 1 - model is still local -- ");
System.out.println("DBG 1 - HttpSession.model.clicks = " + ((Model)httpSession.getAttribute("model")).getClicks());
System.out.println("DBG 1 - this.model.clicks = " + model.getClicks());
model = (Model)httpSession.getAttribute("model");
fetchIP();
model.setName(name);
model.setClicks(model.getClicks() + 1);
System.out.println("---------------------- DBG 2 -------------------");
System.out.println("DBG 2 - model loaded from httpSession -- ");
System.out.println("DBG 2 - HttpSession.model.clicks = " + ((Model)httpSession.getAttribute("model")).getClicks());
System.out.println("DBG 2 - this.model.clicks = " + model.getClicks());
}
在控制器中,我从HTTPSession属性对象和当前对象中打印click值,以确保它是相同的。
运行应用程序:
我使用Fiddler启用端口解析,因为我在开发PC上运行群集,因此只有一个IP可用 在顶部,我列出了我们命中的服务器的IP(x.x.x.212:7003)。
每次点击,Hello XXXX *之后的计数器都会增加。
现在,如果我切换到端口7004并且我希望下一个计数器为4,但它会回到0
以下是服务器的输出
:7003
---------- Model constructor -----------
----------------- Controller initializing @ Wed Apr 04 09:05:46 EDT 2018 -------
DBG - Wed Apr 04 09:05:56 EDT 2018 HttpSession -- **k6yQw2Hd_EKgK7OanKloEHbCKkEMpv9TQfxrXd-8tj05fNlnH8O-!-1996882782!-1285789144!1522847146467**
---------------------- DBG 1 -------------------
DBG 1 - model is still local --
DBG 1 - HttpSession.model.clicks = 0
DBG 1 - this.model.clicks = 0
---------------------- DBG 2 -------------------
DBG 2 - model loaded from httpSession --
DBG 2 - HttpSession.model.clicks = 1
DBG 2 - this.model.clicks = 1
DBG - Wed Apr 04 09:05:57 EDT 2018 HttpSession -- k6yQw2Hd_EKgK7OanKloEHbCKkEMpv9TQfxrXd-8tj05fNlnH8O-!-1996882782!-1285789144!1522847146467
---------------------- DBG 1 -------------------
DBG 1 - model is still local --
DBG 1 - HttpSession.model.clicks = 1
DBG 1 - this.model.clicks = 1
---------------------- DBG 2 -------------------
DBG 2 - model loaded from httpSession --
DBG 2 - HttpSession.model.clicks = 2
DBG 2 - this.model.clicks = 2
...
DBG - Wed Apr 04 09:05:59 EDT 2018 HttpSession -- k6yQw2Hd_EKgK7OanKloEHbCKkEMpv9TQfxrXd-8tj05fNlnH8O-!-1996882782!-1285789144!1522847146467
---------------------- DBG 1 -------------------
DBG 1 - model is still local --
DBG 1 - HttpSession.model.clicks = 7
DBG 1 - this.model.clicks = 7
---------------------- DBG 2 -------------------
DBG 2 - model loaded from httpSession --
DBG 2 - HttpSession.model.clicks = 8
DBG 2 - this.model.clicks = 8
:7004
DBG - Wed Apr 04 09:06:12 EDT 2018 HttpSession --k6yQw2Hd_EKgK7OanKloEHbCKkEMpv9TQfxrXd-8tj05fNlnH8O-!-1285789144!-1996882782!1522847146467
---------------------- DBG 1 -------------------
DBG 1 - model is still local --
DBG 1 - HttpSession.model.clicks = 0
DBG 1 - this.model.clicks = 0
---------------------- DBG 2 -------------------
DBG 2 - model loaded from httpSession --
DBG 2 - HttpSession.model.clicks = 1
DBG 2 - this.model.clicks = 1
...
DBG - Wed Apr 04 09:06:17 EDT 2018 HttpSession --k6yQw2Hd_EKgK7OanKloEHbCKkEMpv9TQfxrXd-8tj05fNlnH8O-!-1285789144!-1996882782!1522847146467
---------------------- DBG 1 -------------------
DBG 1 - model is still local --
DBG 1 - HttpSession.model.clicks = 11
DBG 1 - this.model.clicks = 11
---------------------- DBG 2 -------------------
DBG 2 - model loaded from httpSession --
DBG 2 - HttpSession.model.clicks = 12
DBG 2 - this.model.clicks = 12
查看会话ID,它似乎是同一个会话
在7003
k6yQw2Hd_EKgK7OanKloEHbCKkEMpv9TQfxrXd-8tj05fNlnH8O - ! - !1996882782 -1285789144 1522847146467
在7004
k6yQw2Hd_EKgK7OanKloEHbCKkEMpv9TQfxrXd-8tj05fNlnH8O - ! - !1285789144 -1996882782 1522847146467
,使用JVM Hash镜像
端口7003上的-1996882782!-1285789144
端口7004上的-1285789144!-1996882782
但是,模型对象不会被继承,或者在端口更改时重新初始化。
如果您有时间阅读本文,我感谢您的耐心和最终的帮助。
这是Oracle支持部门的回复:
目标:
会话对象属性更改似乎不会在群集中复制。
解决方案:
如果会话对象属性发生更改,请始终使用setAttribute,然后使用内存中复制在对象中复制对象及其属性。
同样,使用removeAttribute从会话对象中删除属性。