Java Servlet
Java servlet 本质上是扩展服务器功能的 Java 程序。它们不仅限于 Web 服务器,但在这种上下文中被提及的最多。几乎所有关于 servlet 的参考都将它们视为 CGI 脚本的替代品,因此最容易将它们视为执行 CGI 功能的 Java 程序。
关于 servlet 最吸引人的地方在于它们声称的性能。传统的 CGI 脚本(用 Perl、C 等编写)都有一个缺点,即每次调用脚本都必须创建一个新进程。进程创建和管理的开销对于负载重的服务器来说可能非常繁重。Servlet 通过为每个请求创建一个线程而不是一个完整的进程来解决这个问题。为每个 servlet 创建一个单独的进程,然后对 servlet 的请求会导致创建一个线程来处理它。
听起来不错,但是如何在 Linux 上使用 servlet 呢?好吧,您需要一个支持 servlet 和 Java 虚拟机的 Web 服务器。在 Web 服务器方面有几种选择。Sun 的 Java Web Server 可能会在 Linux 上运行(因为它是用 Java 编写的),但它是商业软件。在本文中,我们将使用 Apache,因为它免费且被广泛使用。这意味着我们需要 Apache 的 servlet 扩展。Livesoftware 制作了一款名为 JRun 的产品,我听说过很多关于它的好评,但同样,我们将严格坚持 Apache 阵营,并使用他们的 mod_jserv 扩展。
接下来,您需要为您的系统选择一个 JDK(Java 开发工具包)。同样,有几种选择。值得一提的是 Blackdown JDK (http://java.blackdown.org/) 和新的 OpenGroup JDK (http://www.camb.opengroup.org/RI/java/linux/),它使用原生线程实现。由于线程对于 servlet 性能非常重要,因此您选择的 JDK 可能会显着影响性能。我使用了 Blackdown JDK,因为我对它很熟悉,并且知道它很稳定。但是,如果您有时间,OpenGroup 的工作值得研究。
要在您的系统上启动并运行 servlet,请按照以下步骤操作。请注意,我假设您选择的 JDK 已安装并正常工作。
第一步是下载最新版本的 Apache (https://apache.ac.cn/) 和最新版本的 JServ (http://java.apache.org/)。在撰写本文时,它们分别是 1.3.1 和 0.9.11。
第二步是使用 tar 命令解压每个压缩包
tar -zxvf apache_1.3.1.tar.gz tar -zxvf jserv0.9.11.tar.gz
第三步是使用 JServ 模块编译 Apache。在 Apache 配置中指定您需要的任何选项,但请确保包含 --add-module 开关以指定 mod_jserv.c 文件的位置。这是一个简单的 Apache 配置命令示例
./configure --prefix=/usr/local/apache --add-module=/usr/local/src/ jserv0.9.11/mod_jserv.c这将自动将 JServ 模块添加到您的 Apache 配置中。配置完成后,继续执行 make 和 install 命令来安装包
make make install第四步是编译 JServ。在编译 JServ 之前,您必须选择一个安装位置。如果您对第二步中解压它的目录感到满意,那么您就设置好了。否则,只需将 JServ 目录移动到您希望它驻留的任何位置。为了保持整洁,您可能希望将其放在您的 Apache 安装树中。
接下来,需要设置 CLASSPATH 变量。即使 JServ 文档建议这可能不是必需的,但我发现除非显式设置,否则包无法编译。CLASSPATH 变量必须指定 JDK 类和 JServ 包中包含的 JSDK 类的路径。我的 JDK 位于 /usr/local/jdk1.1.6 中,JDK 类归档文件位于此树的 /lib 目录中。我的 JServ 位于 /usr/local/apache/jserv 中,因此我的 CLASSPATH 变量将按如下方式设置
export CLASSPATH=/usr/local/jdk1.1.6/lib/\ classes.zip:/usr/local/apache/jserv/servclasses.zip
设置完成后,切换到 JServ 目录并编译它
cd /usr/local/apache/jserv make第五步是配置 Apache。JServ 需要将许多配置参数添加到您的 Apache 服务器配置文件中。这些文件通常位于您的 Apache 安装树的 /etc 目录中。使用您喜欢的编辑器打开 httpd.conf 并添加以下配置指令
ServletBinary:java 二进制文件的完整路径名。例如
ServletBinary /usr/local/jdk1.1.6/bin/java
ServletClassPath:指定各种 Java 类的路径。JServ 要求您指定 JDK、JSDK 和 JServ 类的路径。例如
ServletClassPath\ /usr/local/jdk1.1.6/lib/classes.zip #path to the JDK classes ServletClassPath\ /usr/local/apache/jserv/servclasses.zip #path to the JSDK classes ServletClassPath /usr/local/apache/jserv/classes #path to the JServ classesServletAlias:这是最重要的指令之一,因为它配置了 servlet 的位置以及如何访问它们。指令的语法是
ServletAlias uri directory_or_filename其中 uri 参数指定如何通过 URL 访问您的 servlet,第二个参数指向 servlet 的实际位置。第二个参数可以指定包含 servlet 的目录,也可以指定包含 servlet 集合的 ZIP/JAR 文件。
例如,如果您的 ServletAlias 指令是
ServletAlias /servlets /usr/local/apache/servlets
那么寻址您的 servlet 的 URL 将类似于 http://yourhostname/servlets,而实际的 servlet 将位于 /usr/local/apache/servlets 目录中。
ServletProperties:给出包含 servlet 属性的文件的位置。路径可以是绝对路径,也可以是相对于 Apache 服务器根目录的路径,如果未指定,则默认为
conf/servlets.properties
可以在属性文件中设置许多属性。可以使用以下语句将参数传递给所有 servlet
servlets.default.initArgs=arg1=val1,arg2=val2,...可以按如下方式将参数传递给单个 servlet
servlet.servletname.initArgs=arg1=val1,arg2=val2,...第六步也是最后一步是启动 Apache。好吧,您现在应该准备就绪了,所以进入您的 Apache 树的 /sbin 目录并启动服务器
cd /usr/local/apache/sbin ./apachectl start
Servlet 接口为所有 servlet 提供了基础。所有 servlet 都必须实现此接口,或者是实现此接口的类的扩展。Servlet 包提供了一个名为 HttpServlet 的类,它实现了 Servlet 接口,因此作为 servlet 开发人员,大部分工作都为您完成了。Servlet 接口允许创建通用 servlet,但我们只关注如何创建充当 CGI 脚本的 servlet。为此,您只需担心 HttpServlet 类。
让我们逐步了解列表 1 中显示的简单 servlet 的代码,看看它是如何工作的。此 servlet 覆盖了 HttpServlet 类提供的 doGet 方法。当客户端向 servlet 发出 GET 请求时,将调用 doGet。在这里,我们让 servlet 响应一个简单的网页,其中给出了标准的“Hello World”消息。doGet 方法获取两个对象,HttpServletRequest 和 HttpServletResponse,它们封装了允许 servlet 从客户端获取信息并与客户端通信的信息。例如,HttpServletResponse 对象包含一个 PrintWriter 对象,该对象可用于将信息发送回客户端。在本例中,我们使用它将我们的“Hello World”消息发送回客户端。我们还使用 HttpServletResponse 对象的 setContentType 方法来通知客户端它将从 servlet 接收 text/HTML 数据。
现在我们已经看到了一个简单的示例,让我们稍微退后一步,看看 HTTP Servlet 是如何工作的。扩展 HttpServlet 类的 Servlet 通过其 service 方法处理所有客户端请求。service 方法理解标准 HTTP 请求,并调用适当的方法来处理每个请求。在上面的示例中,service 方法将识别 GET 请求并相应地调用 doGet 方法。类似地,提供了 doPost、doPut 和 doDelete 方法来处理其他类型的 HTTP 请求。
servlet 的生命周期从调用 servlet 的 init 方法开始。Web 服务器在加载 servlet 时但在处理任何客户端请求之前调用 init 方法。init 方法在加载 servlet 时仅调用一次。因此,如果需要在 servlet 开始处理请求之前执行任何初始化,请按如下方式重载 init 方法
public void init(ServletConfig config) throws ServletException { super.init(config); ... }
init 方法传递一个包含有关 servlet 的配置信息的对象。最好存储此对象,以便在客户端需要时可以使用它。最简单的方法是调用超类的 init 方法并将 ServletConfig 对象传递给它。关于 servlet 初始化的最后一个提示:如果您的初始化失败并且 servlet 无法处理客户端请求,请抛出 UnavailableException 异常。初始化完成后,servlet 启动,service 方法处理客户端请求。
最后,当 servlet 从 Web 服务器卸载时,将调用其 destroy 方法。Web 服务器会等待所有 service 方法完成或经过一定秒数(以先到者为准),然后再调用 destroy 方法。在长时间运行的 service 方法的情况下,destroy 方法可能会在所有 service 调用完成之前被调用。
这种情况可以使用一些附加方法和变量来处理。首先,创建一个变量来跟踪已调用了多少个 service 方法,并提供同步方法来增加和减少计数器,以及一个返回您的 service 计数器值的方法。
public MyServlet extends HttpServlet { private int numServices = 0; protected synchronized void enterService() { numServices++; } protected synchronized void exitService() { numServices-; } protected synchronized int serviceCount() { return numServices; } }
现在,我们有了这些计数器,我们需要修改 service 方法以相应地递增和递减它们。这是通过在 service 方法的顶部添加对 enterService 方法的调用来完成的,调用超类的 service 方法来处理实际工作,然后通过调用 exitService 方法来递减计数器。
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { enterService(); try { super.service(req, resp); } finally { exitService(); } }接下来,需要一个标志来确定 servlet 是否正在关闭过程中。为了配合这个标志,使用访问器方法来设置标志并返回其值。
MyServlet continued { private Boolean exiting; protected setExiting(Boolean flag) { exiting = flag; } protected Boolean isExiting() { return exiting; } }现在,destroy 方法应首先检查是否有任何 service 方法尚未完成,然后循环直到所有 service 方法都完成。
public void destroy() { if (serviceCount() > 0) { setShuttingDown(true); } while(serviceCount() > 0) { try { Thread.sleep(interval); } catch (InterruptedException e) { } } }最后,修改您的任何可能长时间运行的方法,以检查 servlet 是否正在关闭,并采取相应的措施。
public void doPost(...) { /* You could do something like this or put * the check into a loop * that takes a long time */ if(!isExiting) { ... } }这就是全部内容:快速介绍如何在您的 Linux 机器上启动并运行 servlet 并编写一些简单的 servlet。如果您想了解更多关于编写 servlet 的信息,可以找到深入介绍它们的书籍。我还建议查看 Sun 网站上提供的 Java 教程;它包含一个不错的 servlet 介绍,我在开始学习它们时使用了它。
Doug Welzel 最近在卡内基梅隆大学完成了他的本科学习,并正在继续他的研究生学习。在他的 CMU 职业生涯中,他一直在使用 Linux,并欢迎您在 welzel@andrew.cmu.edu 上提出您的意见。