【问题标题】:Any way to share session state between different applications in tomcat?有什么方法可以在tomcat中的不同应用程序之间共享会话状态?
【发布时间】:2010-10-14 12:51:47
【问题描述】:

我们希望将一个工作应用程序拆分为两个不同的.war 文件,以便能够更新一个应用程序而不影响另一个应用程序。每个 webapp 都会有不同的 UI、不同的用户和不同的部署计划。

最简单的路径似乎是共享同一个会话,所以如果app A设置session.setAttribute("foo", "bar") app B就能看到。

有没有办法为同一个 Tomcat 实例中的两个应用共享 HttpSession 状态?

我们的应用程序在专用的 Tomcat 5.5 上运行,没有其他应用程序在同一个 tomcat 实例上运行,因此有关会话共享的任何安全问题都不是问题。我们正在运行多个 Tomcat 实例,但平衡器正在使用粘性会话。

如果不可能或此会话共享是一个非常糟糕的主意,请发表评论。

【问题讨论】:

    标签: java session tomcat jakarta-ee


    【解决方案1】:

    conf/context.xml

    <Context sessionCookiePath="/">
      ...
      <Manager className="org.redisson.tomcat.RedissonSessionManager"
                   configPath="${catalina.base}/conf/redisson.yml"
                   readMode="REDIS" />
    </Context>
    

    conf/redisson.yml

    singleServerConfig:
      address: "redis://<host>:6379"
    

    雷迪森download

    sessionCookiePath="/" 使 Tomcat 对不同的 Web 应用程序使用相同的会话 ID。 RedissonSessionManager 使会话保持在“共享空间”中


    我无法在共享 context.xml 中使用 org.apache.catalina.session.FileStore PersistentManager 获得预期的结果,我在后台过期监视器线程中遇到了会话反序列化问题。它未能对会话进行反序列化,因为它在类路径中使用了没有 webapp 可序列化模型的通用类加载器。从理论上讲,PersistentManager 可以在 WEB-INF/context.xml 中为每个 Web 应用程序单独配置(以具有正确的类路径),但我未能使其工作。

    org.apache.catalina.session.JDBCStore PersistentManage 很有希望,因为它公开了会话的last_access 列,因此不需要反序列化session_data,但它一直在保存app_name,导致相同的会话ID 被写入不同的网络应用程序的不同行。因此会话数据没有存储在共享位置。

    Spring Session 有自己的方式来创建会话 ID。我无法找到强制 Spring Session 为不同的 Web 应用程序创建相同会话 ID 的解决方案。

    使用核心 tomcat 会话 ID 生成的解决方案(能够为不同的 Web 应用程序和 RedissonSessionManager(使用会话 ID 作为唯一键存储数据并具有自己的过期机制)生成相同的能力)终于对我有用。该解决方案与@SessionScope spring beans 完美配合。

    【讨论】:

      【解决方案2】:

      如果这两个 webapp 如此紧密耦合以至于它们需要共享对象,那么为什么要将它一分为二呢?即使您在某种程度上独立管理它们,任何体面的构建管理系统都应该能够创建一个用于部署的 WAR 文件。

      像 Aaron 建议的 JNDI 解决方案会起作用,但前提是两个 webapps 都在同一台服务器上运行。如果这些单元是紧密耦合的,并且无论如何您都将在同一台服务器上运行它......不妨只有一个 WAR。

      如果您真的希望他们独立站立,我会认真检查两者之间的数据交换。理想情况下,您希望他们只彼此共享相关数据。这些数据可以通过 POST(或 GET,如果更合适)参数来回传递,您甚至可以考虑使用 cookie。

      【讨论】:

      • 我们正在拆分 web 应用程序,以便能够更新一个应用程序而不影响另一个应用程序。每个 webapp 都会有不同的用户界面、不同的用户和不同的部署计划。
      • 那么你一定要确保它们只通过一些 API 进行通信,而不是通过共享类实例(除非通过 JMX 之类的东西)。
      • 另一个例子是提供多语言翻译的网站,即 es.example.com pt.example.com ru.example.com 等。每个多语言网站都有自己的 servlet 映射,所以它是一个不同的应用程序.但他们可能希望重用一些数据,因为内容已被翻译。
      【解决方案3】:

      对于 Tomcat 8,我使用以下配置在 2 个 web 应用程序之间共享会话:

      conf/context.xml

      <Context sessionCookiePath="/">
          <Valve className="org.apache.catalina.valves.PersistentValve"/>
          <Manager className="org.apache.catalina.session.PersistentManager">
              <Store className="org.apache.catalina.session.FileStore" directory="${catalina.base}/temp/sessions"/>
          </Manager>
          ...
      </Context>
      

      我两次部署相同的简单 webapp log.warlog2.war

      /log
      /log2
      

      我现在可以登录/log 并在/log2 中显示用户,这不适用于tomcat 默认配置。

      设置并读取会话值:

      HttpSession session=request.getSession();  
      session.setAttribute("name",name);
      
      HttpSession session=request.getSession(false);  
      String name=(String)session.getAttribute("name");  
      

      我以这个项目为例:https://www.javatpoint.com/servlet-http-session-login-and-logout-example

      大多数示例/解决方案使用需要更多设置工作的内存数据库:

      【讨论】:

        【解决方案4】:

        我使用python为tomcat开发了session state server

        因此,我不需要更改已经为创建/访问和销毁会话而编写的代码。此外,由于有单独的服务器/服务正在处理和存储会话,因此不需要主集群。在这种情况下,没有会话复制(如在 tomcat 集群中),而是网络农场之间的会话共享。

        【讨论】:

          【解决方案5】:

          此博客文章中描述了一种方法:Session sharing in Apache Tomcat

          总结:在Connector配置中添加emptySessionPath,在Context中添加crossContext

          【讨论】:

          • 这看起来是一个很好的解决方案,但你应该真正包含一些实现细节作为一个有效的答案,以防引用的链接中断。
          【解决方案6】:

          雄猫 8: 我必须这样做:&lt;Context crossContext="true" sessionCookiePath="/"&gt; 在 conf/context.xml 中

          更多关于配置属性的细节here

          然后设置值(如@Qazi 的回答):

          ServletContext servletContext =request.getSession().getServletContext().getContext("contextPath")
          servletContext.setAttribute(variableName,variableValue)
          

          获取值:

          ServletContext servletContext =request.getSession().getServletContext().getContext("contextPath")
          servletContext.getAttribute("user"); 
          

          【讨论】:

          • 您在上下文之间共享数据,但不共享会话数据。完全不同
          • 问题是关于跨多个 webapps 共享会话。
          【解决方案7】:

          如果你想使用 Spring,有一个名为 Spring Session 的项目: https://github.com/spring-projects/spring-session

          引用:“HttpSession - 允许以中立方式替换应用程序容器(即 Tomcat)中的 HttpSession”

          【讨论】:

            【解决方案8】:

            您可以通过上下文根获取 servlet 上下文。

            用于检索变量。

            request.getSession().getServletContext().getContext("/{applicationContextRoot}").getAttribute(variableName)
            

            设置变量:

            request.getSession().getServletContext().getContext("/{applicationContextRoot}").setAttribute(variableName,variableValue)
            

            注意:这两个应用程序应该部署在同一台服务器上。

            如果您发现任何问题,请告诉我

            【讨论】:

            • 这不是会话,而是 servlet 上下文,它与会话的范围和生命周期不同,有两点不同。
            【解决方案9】:

            您不应该为了获得高可用性而以这种方式拆分您的应用。您可以在许多 tomcat 实例上部署整个应用程序。

            【讨论】:

            • 这不是可用性问题。我们想要两场不同的战争,因为每一场都有自己的目的,比如一个管理应用程序和一个用户应用程序。
            • @Serhii:很公平。你没有在你的问题中提到这一点。相反,您说的是in order to be able to update one app without affecting the other。这是我回答的基础。
            【解决方案10】:

            需要注意的一点是,两个 Web 应用程序将使用不同的类加载器。如果你想共享对象,他们需要使用来自同一个类加载器的相同版本的类(否则你会得到 LinkageErrors)。这意味着要么将它们放在由两个 Web 应用程序共享的类加载器中(例如系统类路径),要么使用序列化来有效地耗尽并用正确的类版本重新构建正确的类加载器中的对象。

            【讨论】:

              【解决方案11】:

              你不应该共享 HttpSession;但您可以共享其他对象。例如,您可以register an object via JNDI 并在您的所有应用程序中访问同一个对象(数据库使用它来池连接)。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2011-01-25
                相关资源
                最近更新 更多