在应用中嵌入Tomcat

很多 Java web 应用和服务,包括开源的和商业化的(比如 Alfresco, iRise, Confluence等),都倾向于将 Apache Tomcat Servlet 引擎整个嵌入到他们的分发包中。Atlatisan公司甚至只支持他们自己提供的嵌入式Tomcat 包,不再提供 WAR/EAR 形式的分发包。这些安装包包含了整个 Tomcat 引擎和配置文件,看起来确实有点大材小用。在大多数配置中,默认的配置文件甚至从来不会变动。真的有办法可以在代码中启动 Tomcat 并且只需要 tomcat 的 jar 文件作为依赖么?在下面的教程中,我们将会对 Jetty (Jetty 是一个为此目的而设计的一种嵌入式 servlet 引擎)进行测试,同时还会展示如何将 Jetty 迁移到 Tomcat 。

我开始研究嵌入式 Tomcat 是因为 BigSense 项目,该项目是一个开源 web 服务,用于模拟传感器网络。我的目的是可以将其作为一个标准的 Linux 软件包进行分发,这样就可以作为一个服务启动,而不用依赖于 Tomcat 软件包。下面的例子使用的是 Scala 语言,BigSense 项目用的也是这种开发语言,但是你也可以轻松地将所有源码和概念转换为 Java 语言。

首先,创建一个 trait(类似于 Java 中的接口),里面包含两个简单的功能,用于启动和停止 web 服务器。端口号可以从配置文件中获取。这是我的实现中唯一可配置的了,但是你也可以对 context path 添加配置。

下面是我使用 Scala 对 Jetty 的实现。大部分是直接从 Jetty 的官方文档中摘出来的。所有的静态资源(图片,CSS 和 javascript 脚本)都被直接打包到了 jar 文件中,可以作为类路径的资源进行访问。如果使用构建工具,如 SBT,Gradel 或者 Maven,可以将这些文件放到项目的 src/main/resources 目录下。Jetty 的 WebAppContext 允许调用 setResourceBase 来使用项目的静态资源。这个例子还展示了如何使用给定的 Context 路径来添加一个 Servlet (在这个例子中,只有一个 servlet,匹配根目录,名字为 MasterServlet)。还可以看到一个 EventListener 的例子。web.xml 中的大部分标准配置在 Jetty 中都可以使用代码进行设置。

Tomcat 的实现比较复杂。也没有足够的关于使用嵌入式 Tomcat 和配置代码的文档。在下面的例子中,我创建了一个 org.apache.catalina.startup.Tomcat 实例。当向 Tomcat 中添加 Servlets 时,因为一些原因需要指定它的工作目录。我这里是以一种平台依赖的方式使用系统属性 java.io.tmpdir 来获取一个临时文件夹。(注意:在本地环境下运行的时候会产生一个空的 ./tomcat.8080 目录)。虽然我不清楚怎样添加一个事件监听器,但却意识到了监听器甚至不会使用它持有的 context,因此只是手动调用它而没有使用context。最后,我在 Tomcat 的文档中没有找到类似 Jetty 中的 setResourceBase 方法来获取静态资源,因此只能创建一个自己的 StaticContentServlet,接下来将会看到。

这个获取静态资源的 servlet 只有一些基本功能。只是简单地找到类路径下的资源并返回。难点是正确地设置 Mime-Type。我尝试使用 javax.activation.FileTypeMap 基于扩展名来获取准确得 mime 类型,但是经常会得到错误的结果。因此,对于项目中已知静态文件的 mime 类型通过硬编码进行了实现。

依赖相当简单,只需添加需要的 Tomcat 和 Jetty 包就可以了。下面展示了在一个 buiuld.sbt 文件中的依赖,但这样的配置只能用于 Maven,Gradel或者Ivy。检查一下,然后确认你使用的是最近版本的 Jetty 以及/或者 Tomcat,因为它们可能会有变化。

从这里开始,创建一个 main 函数,然后启动你的服务器将会变得非常简单。我使用了一个名为 sbt-native-packager 的插件来创建 deb 和 rpm 文件,其中会用到相关的初始化脚本或 SystemD 服务文件。这么做允许你像安装一个标准的 Linux 安装包一样来安装 BigSense,作为标准服务独立于系统的 Tomcat,并且不需要多余的 war 或 ear 文件。

当然,这样做也有缺点。比如你有很多 web 应用都按这种方式进行部署,对于每个应用来讲就是启动一个完整 Tomcat 和 JVM 实例。即使 Tomcat 相对来讲(和 JBoss 或者 WebSphere 比起来)是轻量级的,仍然是比较重的,会耗费相当多的资源,这一点在虚拟机上尤为明显。

如果在你的空间中有很多 apps,使用系统中的 Tomcat 软件包,然后使用诸如 Fabric 之类的部署系统来维护、更新和部署你的 web 应用可能会更好点。如果你需要将应用打包给第三方,则使用嵌入式 Tomcat 是一个更好的解决方案。然而,当发现安全漏洞时,对于更新安装包来讲你就要小心了。对于嵌入式方法来讲,添加一个类似SSL的东西是比较复杂的,更好的解决方案是将类似 HAProxy 或者 Nginx 的软件作为前端代理来处理用户的 SSL 请求。

如果你是从零开始开发一个应用,应该考虑一下避免完全使用 Servlet 模式。在 JVM上,有很多为 web 服务和应用设计的异步框架,比如 Spray 和 Netty,远超这个设计于 1995 年的 HTTP Servlet API 。

如果你由于软件分发打算学习如何将 Tomcat 嵌入到 web 应用中,希望这篇教程可以帮到你。请时刻牢记以下这点,对于你给出的 jar 包和平台版本的 API 的变化,这些例子可能需要做一些调整来适配这些变化。虽然我只是讲到了 Tomcat 和 Jetty , 其实还有其它的嵌入式 Servlet 引擎,可以用相似的方式实现,甚至对于更新的非 Servlet 引擎(比如 Spray 和 Netty)来讲都有对 Servlet 的包装,这样你就可以在一些比较老的 web 应用上使用它们了。

原文发布于微信公众号 - java一日一条(mjx_java)

原文发表时间:2016-06-14

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏未闻Code

使用Jenkins自动部署博客

这篇文章比较简单,适合初学持续集成的读者,本文可以帮助你对基于Jenkins的持续集成有一个比较全局的概念。

16020
来自专栏技术博文

PHP连接MySQL数据库的三种方式(mysql、mysqli、pdo)

PHP与MySQL的连接有三种API接口,分别是:PHP的MySQL扩展 、PHP的mysqli扩展 、PHP数据对象(PDO) ,下面针对以上三种连接方式做下...

78940
来自专栏服务器运维

Debian系统快速安装Caddy/PHP7/SQLite及一键安装Typecho

如果我们准备搭建正规网站且准备长期运营的,建议还是选择较为成熟的WEB面板或者脚本安装环境,毕竟通过多年的完善比较成熟,而且有较多的可用文档解决常规问题。且我们...

61350
来自专栏大魏分享(微信公众号:david-share)

对,俺差的是安全! | 从开发角度看应用架构18

在Gartner定义的“第三平台”盛行的年代,html5大行其道。所以http方式访问的应用很多。因此,谈到应用的安全,我们先要了解http的几种认证方式。

11010
来自专栏pangguoming

CentOS7安装openjdk、tomcat和mysql流程介绍

首先是前戏,推荐一个远程工具Xshell和Xftp搭配使用,以下是Xshell的官网  http://www.netsarang.com/products/xs...

44780
来自专栏Vamei实验室

Java网络01 Tomcat初次尝试

Tomcat是一套开源软件,它由Apache Software Foundation(ASF)开发,用于实现Java Servlet和JavaServer Pa...

21690
来自专栏君赏技术博客

我的自动化构建之路之 Jenkins+Fastlane+Github内网测试

可能看到这一篇文章很多人认为 Jenkins就可以实现自动化打包,并且 Fastlane配置 完毕之后打包更加的轻松。干嘛还搞在一起,这不是重复了吗。

37920
来自专栏Web项目聚集地

三周学会小程序第三讲:服务端搭建和免费部署

通过第二讲我们已经知道了怎么快速搭建一个小程序客户端,当然服务端也是必不可少的。登录验证,内容存储等等都离不开服务端。

26910
来自专栏Gaussic

使用IntelliJ IDEA开发SpringMVC网站(一)开发环境 顶

访问GitHub下载最新源码:https://github.com/gaussic/SpringMVCDemo

26610
来自专栏伦少的博客

通过数据库客户端界面工具DBeaver连接Hive

本文讲解如何通过数据库客户端界面工具DBeaver连接hive,并解决驱动下载不下来的问题。

35540

扫码关注云+社区

领取腾讯云代金券