servlet容器的时代已经终结

jopen 9年前
 

在Java的世界里, TomcatJetty 这样的servlet容器可以服务于web应用程序。但是最近新起的非java方案,比如 Node.jsGO ,则提供了其自带的内嵌服务器,这让大家开始意识到 Java 也可以使用内嵌服务器。本文探讨servlet容器为何以现在的方式构建的原因,为什么在一些情况下仍然适用,不过它们至少已经不是最初设计的那样了。

一点历史

1999年Apache Tomcat(之后的Jakarta Tomcat)出现的时候,互联网及其工具还远没有今天这么复杂和专业。因此servlet容器被设计用来服务的用户场景和如今也有非常大的差异。

Servlet容器被定位为托管web应用程序的高可用组件。尽管这在当时不失为一个很好的想法,但是它也带来了一些问题,比如 内存泄露 和类装载问题。

现状

现代应用程序环境通常包括多个服务。无论你是否正在运行一个庞大的应用程序,或者正在致力于基于微服务的架构,我知道你都会同意这一点,对于所有基于web的内容,可用性都及其重要,特别是如果你运行的是在线业务的时候。虽然可用性在15年之前就很重要(以前通常会在流量最小的午夜部署变更,停机时间也是小菜一碟),但是以前客户对性能的期待从来没有像现在这么高。

不匹配的容器简化度

如果你正在运行庞大的web应用程序,你可能会想要坚持使用Jetty,Tomcat, WildFly 或其他的独立实例,因为它们很容易使用。比如,容器通常提供了集成的web接口来部署一个新的.war文件,并且支持基础的监控需求。

但是,如果从批判的角度看,servlet容器只是一个组件,它需要部署,配置和维护。这并不一定是坏事,因为容器的确能解决一定的问题,任何解决问题的工具都值得被部署,配置和维护,不是么?不过,servlet容器有一个问题:容器和其上运行的服务之间耦合太紧。

部署

既然容器只是组件,那么就需要仔细考虑容器实例的部署。容器有发布周期。在2015年,Tomcat 8至今大概有八个版本发布。如果继续这样的趋势,今年大概还有三到四个版本。

因为所有维护工具也有自己的发布周期,这些单独看不应该成为问题。真正的问题是无法在容器内运行的服务不中断的情况下部署新的容器版本。

这种容器及其托管服务之间高度依赖的关系给了我们三种可能的部署场景:

  • 部署新的servlet容器,保留当前托管应用程序的版本
  • 部署新的servlet容器,更新到最新的托管应用程序的版本
  • 部署新的托管应用程序的版本

这三种之中的两种场景通常都需要多个build作业才能完成,这些作业必须顺次执行或者需要相应地组合起来。

使用内嵌的servlet容器的话,你就只能“部署新的托管应用程序的版本”,因为内嵌的容器版本定义在项目的 Maven 或者 Gradle 配置文件里。

部署servlet容器通常并不依赖于容器版本。虽然你可以拷贝一个executable.jar文件到文件系统里并且执行,但是通常还是需要遵照容器的部署API的改变来保证脚本的正确性。这在Tomcat6到8的每个主要版本里都会发生。

单独容器的倡导者告诉我们在容器里运行的应用程序越多,额外消耗就越小。当容器需要更新时,只需要完成这个单独容器的更新就好了,而不需要更新所有运行内嵌容器的实例。虽然这有些道理,但并不完全正确:

当某个应用程序在容器里造成严重破坏的时候,它会轻易影响到容器里运行的别的所有东西。某个单一失败的服务影响到别的不直接相关的服务,这显然不是想要的事情 -- 当这样的问题发生时,会导致环境里发生完全无法预测的行为。

除了部署和维护单独容器的运营之外,还需要关注如下几点:

内存

如果使用自己的硬件,内存现在非常便宜。但是在云环境里,价格通常取决于CPU和内存的使用量,因此价格可能会很贵。

现在你可能并不会考虑运行32-bit的JVM,但是要注意64-bit的JVM要求更多的内存 --平均大概要多30-50%。注意,增加的Java内存使用也会导致 垃圾回收时间的增加 ,因此这不仅仅有关于内存使用量 -- 还有关于性能。

因此,如果你的应用程序或者服务要求少于2GB的内存,32-bit的JVM可以支持的话,那么就应该继续使用32-bit的JVM。使用64bit的JVM来托管许多可以在32-bit JVM上轻松运行的服务,从性能角度而言是不好的方案。

配置

除了配置应用程序和服务,容器本身也需要配置。端口和日志级别是这些配置中最为重要的部分。虽然有些库帮助这些配置尽可能得统一和无缝,但是配置一个嵌入式容器仍然是最简单的方式。

衍生思考

在Google上搜索“embedded Java web server”。特别是服务API或者较小的配置UI,你很可能可以用轻量级的方案来代替重量的Tomcat,Jetty,WildFly等等。

总结

在决定是否坚持使用内嵌式容器的时候,可以试着问问“它依赖于”这个问题。尽管我知道“它依赖于”这个问题比起如今web的性能,扩展性以及可用性标准而言并没有那么重要。

更简单的部署方式, 更高效的内存使用,互不依赖服务间更好的隔离,以及更容易的部署都是使用内嵌容器能带来的优势。

现状就是这样,单独的容器设计用来解决的问题如今遇到的已经不多了。类似Spring Boot这样的框架使得更容易开始使用单独的应用程序。

原文链接: The era of servlet containers is over (翻译:崔婧雯 校对:)

===========================

译者介绍

崔婧雯,现就职于IBM,高级软件工程师,负责IBM WebSphere业务流程管理软件的系统测试工作。曾就职于VMware从事桌面虚拟化产品的质量保证工作。对虚拟化,中间件技术,业务流程管理有浓厚的兴趣。