深入jetty的使用详解
简介:Jetty是一个用Java实现、开源、基于标准的,并且具有丰富功能的Http服务器和Web容器,可以免费的用于商业行为。Jetty这个项目成立于1995年,现在已经有非常多的成功产品基于Jetty,比如ApacheGeromino,JBoss,IBMTivoli,CiscoSESM等。Jetty可以用来作为一个传统的Web服务器,也可以作为一个动态的内容服务器,并且Jetty可以非常容易的嵌入到Java应用程序当中。
特性简介
易用性
易用性是Jetty设计的基本原则,易用性主要体现在以下几个方面:
1.通过XML或者API来对Jetty进行配置;
2.默认配置可以满足大部分的需求;
3.将Jetty嵌入到应用程序当中只需要非常少的代码;
可扩展性
在使用了Ajax的Web2.0的应用程序中,每个连接需要保持更长的时间,这样线程和内存的消耗量会急剧的增加。这就使得我们担心整个程序会因为单个组件陷入瓶颈而影响整个程序的性能。但是有了Jetty:
1.即使在有大量服务请求的情况下,系统的性能也能保持在一个可以接受的状态。
2.利用Continuation机制来处理大量的用户请求以及时间比较长的连接。
另外Jetty设计了非常良好的接口,因此在Jetty的某种实现无法满足用户的需要时,用户可以非常方便地对Jetty的某些实现进行修改,使得Jetty适用于特殊的应用程序的需求。
易嵌入性
Jetty设计之初就是作为一个优秀的组件来设计的,这也就意味着Jetty可以非常容易的嵌入到应用程序当中而不需要程序为了使用Jetty做修改。从某种程度上,你也可以把Jetty理解为一个嵌入式的Web服务器。
--------------------------------------------------------------------------------
部署应用程序
将自己的应用程序部署到Jetty上面是非常简单的,首先将开发好的应用程序打成WAR包放到Jetty的Webapps目录下面。然后用如下的命令来启动Jetty服务器:Java?jarstart.jar,在启动服务器后。我们就可以访问我们的应用程序了,Jetty的默认端口是8080,WAR的名字也就是我们的应用程序的RootContext。例如一个典型的URL就是:http://127.0.0.1:8080/sample/index.jsp。
--------------------------------------------------------------------------------
如何将Jetty嵌入到程序当中
将Jetty嵌入到程序当中是非常简单的,如代码1所示:首先我们创建一个Server对象,并设置端口为8080,然后为这个Server对象添加一个默认的Handler。接着我们用配置文件jetty.xml对这个server进行设置,最后我们使用方法server.start()将Server启动起来就可以了。从这段代码可以看出,Jetty是非常适合用于作为一个组件来嵌入到我们的应用程序当中的,这也是Jetty的一个非常重要的特点。
清单1.代码片断
publicclassJettyServer{
publicstaticvoidmain(String[]args){
Serverserver=newServer(8080);
server.setHandler(newDefaultHandler());
XmlConfigurationconfiguration=null;
try{
configuration=newXmlConfiguration(
newFileInputStream("C:/development/Jetty/jetty-6.1.6rc0/etc/jetty.xml"));
}catch(FileNotFoundExceptione1){
e1.printStackTrace();
}catch(SAXExceptione1){
e1.printStackTrace();
}catch(IOExceptione1){
e1.printStackTrace();
}
try{
configuration.configure(server);
server.start();
}catch(Exceptione){
e.printStackTrace();
}
}
}
接下来我们分析一下JettyServer是如何启动的。首先我们注意到Server类,这个类实际上继承了HttpServer,当启动Jetty服务器的时候,就是说,在Jetty根目录下的命令行下如果输入java-jarstart.jaretc/jetty.xml,注意这里有一个配置文件jetty.xml做为运行参数,这个参数也可以是其它的配置文件,可以是多个XML配置文件,其实这个配置文件好比我们使用Struts时的struts-config.xml文件,将运行Server需要用到的组件写在里面,比如上一节中HttpServer的配置需要的组件类都可以写在这个配置文件中。按上述方法启动JettyServer时,就会调用Server类里面的main方法,这个入口方法首先会构造一个Server类实例(其实也就构造了一个HttpServer),创建实例的过程中就会构造XmlConfiguration类的对象来读取参数配置文件,之后再由这个配置文件产生的XmlConfiguration对象来配置这个Server,配置过程其实是运用了Java的反射机制,调用Server的方法并传入配置文件中所写的参数来向这个Server添加HttpListener,HttpContext,HttpHandler,以及WebApplication(对应于我们的Web应用)。
--------------------------------------------------------------------------------
Jetty的Continuation机制
讨论Jetty的Continuation机制,首先需要提到Ajax技术,Ajax技术是当前开发Web应用的非常热门的技术,也是Web2.0的一个重要的组成部分。Ajax技术中的一个核心对象是XMLHttpRequest对象,这个对象支持异步请求,所谓异步请求即是指当客户端发送一个请求到服务器的时候,客户端不必一直等待服务器的响应。这样就不会造成整个页面的刷新,给用户带来更好的体验。而当服务器端响应返回时,客户端利用一个Javascript函数对返回值进行处理,以更新页面上的部分元素的值。但很多时候这种异步事件只是在很小一部分的情况下才会发生,那么怎么保证一旦服务器端有了响应之后客户端马上就知道呢,我们有两种方法来解决这个问题,一是让浏览器每隔几秒请求服务器来获得更改,我们称之为轮询。二是服务器维持与浏览器的长时间的连接来传递数据,长连接的技术称之为Comet。
大家很容易就能发现轮询方式的主要缺点是产生了大量的传输浪费。因为可能大部分向服务器的请求是无效的,也就是说客户端等待发生的事件没有发生,如果有大量的客户端的话,那么这种网络传输的浪费是非常厉害的。特别是对于服务器端很久才更新的应用程序来讲,比如邮件程序,这种浪费就更是巨大了。并且对Server端处理请求的能力也相应提高了要求。如果很长时间才向Server端发送一次请求的话,那么客户端就不能的得到及时的响应。
如果使用Comet技术的话,客户端和服务器端必须保持一个长连接,一般情况下,服务器端每一个Servlet都会独占一个线程,这样就会使得服务器端有很多线程同时存在,这在客户端非常多的情况下也会对服务器端的处理能力带来很大的挑战。
Jetty利用Java语言的非堵塞I/O技术来处理并发的大量连接。Jetty有一个处理长连接的机制:一个被称为Continuations的特性。利用Continuation机制,Jetty可以使得一个线程能够用来同时处理多个从客户端发送过来的异步请求,下面我们通过一个简化的聊天程序的服务器端的代码来演示不使用Continuation机制和使用Continuation的差别。
清单2.Continuation机制
publicclassChatContinuationextendsHttpServlet{
publicvoiddoPost(HttpServletRequestrequest,HttpServletResponseresponse){
postMessage(request,response);
}
privatevoidpostMessage(HttpServletRequestrequest,HttpServletResponseresponse)
{
HttpSessionsession=request.getSession(true);
Peoplepeople=(People)session.getAttribute(session.getId());
if(!people.hasEvent())
{
Continuationcontinuation=
ContinuationSupport.getContinuation(request,this);
people.setContinuation(continuation);
continuation.suspend(1000);
}
people.setContinuation(null);
people.sendEvent(response);
}
}
大家注意到,首先获取一个Continuation对象,然后把它挂起1秒钟,直到超时或者中间被resume函数唤醒位置,这里需要解释的是,在调用完suspend函数之后,这个线程就可处理其他的请求了,这也就大大提高了程序的并发性,使得长连接能够获得非常好的扩展性。
如果我们不使用Continuation机制,那么程序就如清单3所示:
清单3.不使用Continuation机制
publicclassChatextendsHttpServlet{
publicvoiddoPost(HttpServletRequestrequest,HttpServletResponseresponse){
postMessage(request,response);
}
privatevoidpostMessage(HttpServletRequestrequest,HttpServletResponseresponse)
{
HttpSessionsession=request.getSession(true);
Peoplepeople=(People)session.getAttribute(session.getId());
while(!people.hasEvent())
{
try{
Thread.sleep(1000);
}catch(InterruptedExceptione){
e.printStackTrace();
}
}
people.setContinuation(null);
people.sendEvent(response);
}
}
大家注意到在等待事件发生的时间里,线程被挂起,直到所等待的事件发生为止,但在等待过程中,这个线程不能处理其他请求,这也就造成了在客户端非常多的情况下服务器的处理能力跟不上的情况。下面我们解释一下Jetty的Continuation的机制是如何工作的。
为了使用Continuatins,Jetty必须配置为使用它的SelectChannelConnector处理请求。这个connector构建在java.nioAPI之上,允许它维持每个连接开放而不用消耗一个线程。当使用SelectChannelConnector时,ContinuationSupport.getContinuation()提供一个SelectChannelConnector.RetryContinuation实例(但是,您必须针对Continuation接口编程)。当在RetryContinuation上调用suspend()时,它抛出一个特殊的运行时异常--RetryRequest,该异常传播到servlet外并且回溯到filter链,最后被SelectChannelConnector捕获。但是不会发送一个异常响应给客户端,而是将请求维持在未决Continuations队列里,则HTTP连接保持开放。这样,用来服务请求的线程返回给ThreadPool,然后又可以用来服务其他请求。暂停的请求停留在未决Continuations队列里直到指定的过期时间,或者在它的Continuation上调用resume()方法。当任何一个条件触发时,请求会重新提交给servlet(通过filter链)。这样,整个请求被"重播"直到RetryRequest异常不再抛出,然后继续按正常情况执行。
--------------------------------------------------------------------------------
Jetty的安全性
为了防止任何人都有权限去关闭一个已经开启的Jetty服务器,我们可以通过在启动Jetty服务器的时候指定参数来进行控制,使得用户必须提供密码才能关闭Jetty服务器,启动Jetty服务器的命令如下所示:
java-DSTOP.PORT=8079-DSTOP.KEY=mypassword-jarstart.jar
这样,用户在停止Jetty服务器的时候,就必须提供密码“mypassword”。
--------------------------------------------------------------------------------
总结
Jetty是一个非常方便使用的Web服务器,它的特点在于非常小,很容易嵌入到我们的应用程序当中,而且针对Web2.0的Ajax技术进行了特别的优化,这也使得我们的使用Ajax的应用程序可以拥有更好的性能。
相关文章
- 《深入理解mybatis原理》 MyBatis的一级缓存实现详解 及使用注意事项
- oboe 从使用到源码详解
- Mysql索引分类及其使用实例详解
- linux nohup 使用详解程序员
- iOS—-CocoaPods的安装、使用和,原理+参考流程+常见问题详解手机开发
- 使用Maven把war包部署到远程tomcat服务器详解架构师
- 使用DOMParser 解析xml的Java代码详解编程语言
- Struts2_day03讲义_使用Struts2完成对客户查询的优化操作详解编程语言
- Java 在使用迭代器迭代集合的过程中的注意事项详解编程语言
- Go中的http服务流程、路由以及http中间件的使用详解编程语言
- 函数使用七:AUTHORITY_CHECK_RFC详解编程语言
- Linux虚拟机中如何使用root账户(linux虚拟机root)
- MySQL 实例的详解及使用方法(mysql实例)
- Linux传输文件工具SCP:使用选择技巧(linuxscp选择)
- Redis使用深入解析——快速实现数据存储与读取(redis使用详解)
- MySQL的双光标使用技巧详解(mysql中双光标怎么用)
- 深入探索Redis的多重使用方式(redis设置多个命令)
- 深入Javascript函数、递归与闭包(执行环境、变量对象与作用域链)使用详解
- 深入多线程之:Reader与WriteLocks(读写锁)的使用详解
- 深入多线程之:Wait与Pulse的使用详解
- 深入SQLite多线程的使用总结详解
- linuxc查找使用库的cflags与libs的方法详解
- 深入ORACLE变量的定义与使用的详解
- 深入mysql存储过程中表名使用参数传入的详解
- 深入php中var_dump方法的使用详解
- node.js中的require使用详解