Tomcat集群环境的架构问题

Rai*_*lam 8 java architecture tomcat web-applications cluster-computing

我正在研究我们拥有认证机制的项目.我们在身份验证机制中遵循以下步骤.

  1. 用户打开浏览器并在文本框中输入他/她的电子邮件,然后单击登录按钮.
  2. 请求转到服务器.我们生成一个随机字符串(例如,123456)并向用户的Android/iPhone发送通知,并使当前线程在该wait()方法的帮助下等待.
  3. 用户在他/她的手机上输入密码并单击他/她手机上的提交按钮.
  4. 一旦用户单击提交按钮,我们就会在服务器上发送Web服务并传递先前生成的字符串(例如,123456)和密码.
  5. 如果密码对于先前输入的电子邮件是正确的,我们将该notify()方法调用到先前等待的线程并发送成功作为响应并且用户进入我们的系统.
  6. 如果密码对于先前输入的电子邮件不正确,我们将该notify()方法调用到先前等待的线程并发送失败作为响应并向用户显示无效的凭证消息.

一切都很好,但最近我们搬到了集群环境.我们发现即使在用户回复并且无限制的等待时间之后,某些线程也不会得到通知.

对于服务器,我们使用Tomcat 5.5,我们遵循Apache Tomcat 5.5 Servlet/JSP容器来创建tomcat集群环境.

答案::可能的问题和解决方案

可能的问题是群集环境中的多个JVM.现在,我们还将聚集的Tomcat URL与生成的字符串一起发送到用户Android应用程序.

当用户单击回复按钮时,我们将生成的字符串与聚集的Tomcat URL一起发送,因此在这种情况下,两个请求都将转到同一个JVM,并且它可以正常工作.

但我想知道是否有针对上述问题的单一解决方案.

该解决方案存在问题.如果群集Tomcat崩溃会发生什么? 负载均衡器将向第二个集群Tomcat发送请求,同样会出现同样的问题.

And*_*ock 9

您的问题的根本原因是Java EE被设计为以不同的方式工作 - 尝试阻止/等待服务线程是重要的禁忌之一.我先说明理由,然后再解决问题.

Java EE(Web和EJB层)旨在扩展到非常大的规模(群集中的数百台计算机).但是,为了做到这一点,设计师必须做出以下假设,这些假设是如何编码的具体限制:

  • 交易是:

    1. 短暂的(例如,不要阻止或等待大于一秒钟的时间)
    2. 彼此独立(例如线程之间没有通信)
    3. 对于EJB,由容器管理
  • 所有用户状态都保存在特定的数据存储容器中,包括:

    1. 通过例如JDBC访问的数据存储.您可以使用传统的SQL数据库或NoSQL后端
    2. 有状态会话bean,如果使用EJB.将它们视为将其字段持久保存到数据库的Java Bean.有状态会话bean由容器管理
    3. Web会话这是一个键值存储(有点像NoSQL数据库,但没有扩展或搜索功能),可以在特定用户的会话中保留数据.它由Java EE容器管理,具有以下属性:

      1. 如果节点在群集中崩溃,它将自动重定位
      2. 用户可以拥有多个当前的Web会话(即在两个不同的浏览器上)
      3. 当用户通过注销结束会话或者会话处于非活动状态超过可配置超时时,Web会话结束.
      4. 存储的所有值必须是可序列化的,以便在群集中的节点之间保留或传输它们.

如果我们遵循这些规则,Java EE容器可以成功管理集群,包括关闭节点,启动新节点和迁移用户会话,而无需任何特定的开发人员代码.开发人员编写图形界面和业务逻辑 - 所有'管道'都由可配置的容器功能管理.

此外,在运行时,Java EE容器可以由一些非常复杂的软件进行监视和管理,这些软件可以跟踪实时系统上的应用程序性能和行为问题.

<snark>嗯,这就是理论.实践表明,错过了非常重要的限制,这导致了AOSP和代码注入技术,但这是另一个故事</ snark>

[围绕这个网络进行了很多讨论.关注EJB的是:为什么不鼓励在Java EE容器中生成线程?对于像Tomcat这样的Web容器来说,情况完全相同]

对于这篇文章感到抱歉 - 但这对你的问题很重要.由于线程的限制,您不应阻止Web请求等待另一个稍后的请求.

当前设计的另一个问题是,如果用户断开网络连接,电量耗尽,或者只是决定放弃,会发生什么?想必你会超时,但过了多久?对某些客户来说,这可能会导致满意度问题.如果超时太长,您可能最终阻止Tomcat中的所有工作线程,服务器将冻结.这会打开您的组织以进行拒绝服务攻击.

编辑:在发布更详细的算法描述后改进了建议.

尽管上面讨论了阻止Web工作线程的不良做法以及可能的拒绝服务,但很明显,用户会看到一个小时间窗口,可以对Android手机上的通知作出反应,这可以保持相当小,以增强安全性.此时间窗口也可以保持在Tomcat的响应超时之下.因此可以使用线程阻塞方法.

有两种方法可以解决此问题:

  1. 将解决方案的重点更改为客户端 - 在浏览器上使用Javascript轮询服务器
  2. 集群中节点之间的通信允许节点从Android App接收授权响应以解除阻塞servlet响应的节点的阻塞.

对于方法1,浏览器通过Javascript轮询服务器,并对Tomcat上的Web服务进行AJAX调用; True如果Android应用程序经过身份验证,则会返回AJAX调用.优点:客户端,服务器上的最小实现,服务器上没有线程阻塞.缺点:在等待期间,您必须频繁拨打电话(可能每秒一次 - 用户不会注意到这种延迟),这相当于服务器上的大量呼叫和一些额外负载.

对于方法2,还有选择:

  1. 通过Object.wait()在共享数据存储中可选地存储节点ID,IP或其他标识符来阻止线程:如果是,则接收Android应用程序授权的节点需要:

    1. 找到当前阻止或广播到群集中所有节点的节点
    2. 对于上面1.中的每个节点,发送标识要取消阻止的用户会话的消息.该消息可以通过以下方式发送:

      1. 在每个节点上都有一个仅供内部使用的servlet - 这由执行Android应用程序授权的servlet调用.内部servlet将调用Object.notify正确的线程
      2. 使用JMS pub-sub消息队列向群集的所有成员广播.每个节点都是一个订户,在收到通知后将调用Object.notify()正确的线程.
  2. 轮询数据存储直到线程被授权继续:在这种情况下,所有Android应用程序需要做的是将状态保存在SQL DB中