Spring MVC在JBoss和Tomcat中的优势与实践

15

好的。这又是一个关于行业惯例的问题。

  • Tomcat = Web容器
  • JBoss,WebLogic等 = 应用服务器中带有Web容器(对于JBoss来说,它是基于Tomcat的)

Spring不需要像JBoss这样的应用服务器。如果我们使用JMS等企业服务,我们可以使用独立的系统,如RabbitMQ、ApacheMQ等。

  1. 问题在于为什么人们仍然针对纯Spring应用程序使用JBoss和其他应用服务器?
  2. 使用应用服务器,Spring可以利用什么优势?比如对象池化?应用服务器具体提供了哪些优势?这些如何配置?
  3. 如果不是为Spring/Hibernate等堆栈,应用服务器还用于什么其他目的?(用例)
3个回答

7
实际上,我认为监听JMS可能是应用服务器的最佳原因。独立的消息代理并不能解决问题,因为您仍然需要一个组件来侦听消息。最好的方法是使用MDB。理论上,您可以使用Spring的MessageListenerContainer。但是,这有几个缺点,例如JMS仅支持阻塞读取,因此Spring需要启动自己的线程,这是完全不受支持的(即使在Tomcat中),可能会破坏事务、安全性、命名(JNDI)和类加载(这反过来可能会破坏远程访问)。JCA资源适配器可以自由地执行任何操作,包括通过WorkManager旋转线程。很可能除了JMS(或其他目标)之外还使用了数据库,在这种情况下,您需要XA事务和JTA,换句话说,需要应用服务器。是的,您可以将其打补丁到servlet容器中,但在这一点上,它变得与应用服务器无法区分。
在我看来,反对应用服务器的最大原因是,在规范发布后需要几年时间(这也需要几年时间),直到服务器实现规范并消除了最严重的错误。直到现在,即将发布EE 7之前,我们才有EE 6服务器开始出现,而这些服务器并不完全充斥着错误。有时候甚至变得滑稽,一些供应商不再修复他们EE 6产品线上的错误,因为他们已经忙于即将推出的EE 7系列产品。
编辑:
最后一段的详细解释:
在很多地方,Java EE依赖于所谓的上下文信息。这些信息不是从服务器/容器显式传递给应用程序的参数,而是隐含地“存在”。例如,当前用户用于安全检查。当前事务或连接。当前应用程序用于查找类以惰性加载代码或反序列化对象。或者当前组件(servlet、EJB等)用于执行JNDI查找。所有这些信息都在服务器/容器调用组件(servlet、EJB等)之前设置的线程本地变量中。如果您创建自己的线程,则服务器/容器不知道它们,所有依赖此信息的功能都无法正常工作。您可以通过在生成的线程中不使用这些功能来避免这种情况。
一些链接

http://www.oracle.com/technetwork/java/restrictions-142267.html#threads http://www.ibm.com/developerworks/websphere/techjournal/0609_alcott/0609_alcott.html#spring-4

如果我们查看Servlet 3.0规范,我们会发现:
2.3.3.3 异步处理 仅当请求被通过AsyncContext.dispatch方法分派到容器时,才能为执行初始请求的线程提供Java Enterprise Edition功能,例如第15.2.2节“Web应用程序环境”(第15-174页)和第15.3.1节“在EJBTM调用中传播安全标识”(第15-176页)。通过AsyncContext.start(Runnable)方法直接操作响应对象的其他线程可能会获得Java Enterprise Edition功能。
这是关于异步处理的内容,但是对于自定义线程也适用相同的限制。 public void start(Runnable r) - 此方法导致容器分派一个线程(可能来自受控线程池)来运行指定的Runnable。容器可以将适当的上下文信息传播给Runnable。
同样,这是关于异步处理的内容,但是对于自定义线程也适用相同的限制。

15.2.2  Web应用环境

这种类型的servlet容器应该支持开发人员创建的线程执行此行为,但目前并不要求。在下一个版本的规范中将添加此要求。开发人员应注意,依赖于此能力来创建应用程序线程是不推荐的,因为它是不可移植的。

不可移植意味着它可能在一个服务器上运行,但在另一个服务器上可能无法运行。

当您想要在MDB之外使用JMS接收消息时,可以在javax.jms.MessageConsumer上使用四种方法:

  • #receiveNoWait()方法可以在容器线程中使用,它不会阻塞,但类似于窥视。如果没有消息,则返回null。这不太适合用于监听消息。
  • #receive(long)方法可以在容器线程中使用,它会阻塞。通常不建议在容器线程中进行阻塞等待。同样不太适合用于监听消息。
  • #receive()方法可能会无限期地阻塞。同样不太适合用于监听消息。
  • #setMessageListener()方法是你想要的,当消息到达时会得到回调。然而,除非库能够钩入应用服务器,否则这不会是一个容器线程。钩子只能通过JCA连接到资源适配器来获取应用服务器的访问权限。

所以,是的,它可能起作用,但不能保证,并且有很多可能会出现问题的情况。


2
然而,这种方法有几个缺点,例如JMS仅支持阻塞读取,因此Spring需要自己启动线程,这是完全不受支持的(即使在Tomcat中),可能会破坏事务、安全性、命名(JNDI)和类加载(从而可能会破坏远程访问)。你能告诉我你在哪里找到这个信息的吗?我相信有些人使用Spring与独立的消息代理而没有应用服务器。我正在尝试查找是否有人在任何地方提出了这个问题。并不是说你错了! :-) 我想获得更多的见解。 - Kevin Rave

2
你说的没错,使用Spring框架并不需要一个真正的应用程序服务器(实现所有Java EE规范)。人们不使用像JBoss这样的真正的Java EE应用程序的最大原因是它们在冷启动时间上非常慢,使开发变得很痛苦(热部署仍然效果不佳)。
你可以看到有两个阵营:
  • Java EE
  • Spring Framework。
其中一个阵营相信规范/委员会流程,而另一个阵营则相信慈善独裁者/有机开源流程。两个阵营都有人推行他们的“议程”。
由于这两个阵营就像Emacs与VIM之争, 所以你可能无法得到一个非常客观的答案。
回答你的问题时倾向于Spring:
  1. 理论上,Spring可以减少厂商锁定(尽管我发现事实上相反)。
  2. Spring的最大优势是AspectJ AOP。
  3. 我想看看菲利普的答案。
(开始抱怨)
由于@PhilippeMarschall为Java EE辩护,我会说我已经使用Tomcat+RabbitMQ+Spring路线,并且它运行得非常好。如果您想要正确的JTA+JMS,则@PhilippeMarschall的讨论是有效的,但是通过正确设置Sprig AMQP和像Postgresql这样的良好事务性数据库,这不是什么问题。此外,他关于消息队列事务未绑定/同步到平台事务是错误的,因为Spring支持这一点(并且在我的看法中,使用@Transactional AOP更加优雅)。此外,AMQP比JMS更加出色。

(发牢骚结束)


人们不使用真正的Java EE应用程序,比如JBoss,在冷启动时速度非常慢 - 在我的三年老i7上:“JBoss EAP 6.0.1.GA(AS 7.1.3.Final-redhat-4)在1845ms内启动 - 启动了217项服务中的136项(80项服务是被动或按需提供的)” - Arjan Tijms
我知道JBoss在速度方面有所改进,因为传统上它非常慢。我一定是下载了错误的JBoss AS,因为它让我的Spring MVC应用程序的启动时间增加了一倍。 - Adam Gent
4
没错,我刚刚又试了一遍。JBoss 6.1.0 Alpha和Tomcat 7进行比较。是的,如果没有Web应用程序,JBoss启动更快,但是一旦我部署了我的Spring MVC Web应用程序,JBoss几乎比Tomcat7慢两倍。 - Adam Gent

0

我们正在使用JBoss覆盖Tomcat来管理JNDI数据源和连接池。这样程序员就不需要了解数据库,只需要知道它的JNDI名称。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接