读者对象
对 jetty 比较熟悉,想了解其部署原理的开发人员。
一、预备知识
1、什么是jetty
官方描述:Jetty是一个100%由Java实现的、开源的HTTP服务器和javax.servlet容器,它不仅仅作为一个独立服务软件(如Tomcat)被使用,而且其优良的组件(Componet)设计、高内聚低耦合、高扩展性等特性使得Jetty非常易于作为嵌入式工具使用。由于Jetty构架优秀、实现优雅,所以它被广泛嵌入的到移动设备、工具、框架(frameworks)、应用程序服务器(Application Server)等等领域。
从开发人员的视图看jetty,由三部分组成:
A、start.jar:
一个很独立的jar,43k,包含7个类,主要作用是服务器启动前的准备工作。
B、lib 下的一组jar
jetty的核心jar,每个jar提供不同的功能,如 jetty-jmx.jar 提供JMX功能,jetty-jndi 提供JNDI支持。
C、etc 下的一组xml文件
Jetty提供了类似IoC/DI容器,jetty.xml配置文件就是这个容器的配置文件。
2、jetty 的过人之处
简单、精巧、嵌入式
最快的Servlet服务器之一
可以处理上千个并发连接
基于NIO的 Continuation
websocket 支持
二、jetty 部署概述
1、部署方式介绍
jetty 支持文件方式和目录方式。
2、部署过程描述
先从jetty启动说起,通过 java -jar start.jar 启动之后,start.jar 主要做了如下工作:
1、解析命令行参数,如 -version ,只是屏幕输出各个jar的版本信息,不会启动服务器
2、准备所需的配置文件,默认配置文件在 start.ini 中指定,也可以在命令行中指定
3、设置 classpath,根据 OPTIONS 指定的值,将对应的jar加入到classpath
4、通过反射调用 start.class 指定的值,以启动服务器,start.class的值在 start.config 中指定,文件位于 start.jar\org\eclipse\jetty\start\start.config
默认的启动类是 org.eclipse.jetty.xml.XmlConfiguration,该类解析xml文件,解析的过程就是服务器启动的过程。
org.eclipse.jetty.server.Server 实现了 LifeCycle,所以在 解析时,会调用 Server 的生命周期方法,在 Server 的 start 过程中,会设置线程池(ThreadPool)、连接器(Connector),并调用 dependentBeans 中所有 bean 的生命周期方法,其中有一个 bean 就是 DeploymentManager(部署管理器),在文件 jetty-deploy.xml 中定义。
DeploymentManager 中添加了两个 AppProvider,分别是 ContextProvider 和 WebAppProvider。
ContextProvider:用于部署/监控 contexts 目录,也可指定目录,通过 monitoredDir 属性指定,里面的文件必须是 xml ,并且 xml 中定义的类必须是 ContextHandler 的子类,一般用来定义一个 WebAppContext 实例,该实例对应一个应用(WAR)WebAppProvider:用于部署/监控 webapp 目录,也可指定目录,通过 monitoredDir 属性指定,里面的文件必须是.war或war目录。
DeploymentManager 也 extends LifeCycle,在 start 过程中,会 1、添加部署过程监听器;2、调用注册的 AppProvider,部署应用。
WebAppProvider 的部署:
1、扫描 webapp 目录,满足条件的添加进 _currentScan(在Scanner中),其规则是:1、是一个标准的WAR目录;2、一个.war文件;3、在 contexts 目录中没有对应的xml文件。
2、具体的扫描过程由 Scanner 完成,在方法 scan 中,完成扫描、部署动作,之后启动定时器,循环调用 scan ,以实现热更新。
3、部署过程由 reportAddition 方法触发,在该方法中会触发 Scanner 类中定义的部署监听器,能监听三种状态:文件添加、删除、修改
4、对于添加(即新增部署一个应用),调用 DeploymentManager 的 addApp 方法,在该方法中触发 requestAppGoal,即 Immediately attempt to go to default lifecycle state。
5、应用的各种状态在jetty中用有向图来描述,其包括的状态有:starting,deployed,started,stopping,deploying,undeployed,undeploying 等。每种状态都定义的相应的绑定监听器,如 starting 过程定义了 StandardStarter,以负责具体的部署过程。
6、最后调用 ServletContextHandler 的 startContext 开始部署应用:解析配置,完成 servlet 容器该完成的工作。
ContextProvider 的部署过程类似,它在解析xml过程中产生的 App,其中定义了war的位置,它会根据该war的位置去部署。
三、热部署过程详解
部署所涉及核心类的UML图:
实际上,jetty热部署过程的实现相当的简陋,其大致思路如下:
先对webapps目录进行扫描,确定需要部署的App,这里的App是contexts目录中没有的应用,即没有同名的xml文件,部署完这些App之后,用名为 _currentScan 的 Map存储,key是app的路径,value是该目录的最后修改时间,启动定时器,按照设置的间隔时间循环扫描。扫描时用新产生的 _currentScan 和 _prevScan(与_currentScan相同,表示前一次扫描的结果)进行比对,确定差异,添加、修改、删除,分别再触发不同的监听器。
对于 添加 的应用,直接新增一个部署,对于 删除 的应用,直接删除一个部署,对于 修改 的应用,jetty的做法是先删除,再将应用完全重新部署。
问题:只有当应用目录的最后修改时间改变了,jetty才认为是应用被修改了,再会重新部署应用,而修改应用中的文件是不会引起应用目录的修改时间改变的,所以,在一般情况下,webapps 中的应用不会被检查到修改。
而对于contexts目录下的xml文件,和webapps目录一样,也是启动定时器定时扫描,找出更新过的文件,其策略是,如果文件的最后修改时间不同,则表示应该被更新了,则应重新部署应用。
问题:任何一点小小的改动都能导致xml文件的修改时间变化,比如说在文件中加一个空格,就会导致应用被卸载,然后再被部署。难道不能做得更精致一点?
对于以上的描述,有代码为证,简单罗列如下:
public class DeploymentManager extends AbstractLifeCycle
{
......
protected void doStart() throws Exception
{
if (_useStandardBindings)//如果使用标准的应用生命周期绑定监听器,则添加jetty自带的,否则,用户可自义绑定监听器
{
Log.debug("DeploymentManager using standard bindings");
addLifeCycleBinding(new StandardDeployer());
addLifeCycleBinding(new StandardStarter());
addLifeCycleBinding(new StandardStopper());
addLifeCycleBinding(new StandardUndeployer());
}
// Start all of the AppProviders
for (AppProvider provider : _providers)//默认 _providers 包括两个:ContextProvider 和 WebAppProvider
{
startAppProvider(provider);
}
super.doStart();
}
......
//触发应用整个生命周期绑定
private void requestAppGoal(AppEntry appentry, String nodeName)
{
Node destinationNode = _lifecycle.getNodeByName(nodeName);
// Compute lifecycle steps
Path path = _lifecycle.getPath(appentry.lifecyleNode,destinationNode);
if (path.isEmpty())
{
// nothing to do. already there.
return;
}
// Execute each Node binding. Stopping at any thrown exception.
try
{
Iterator<Node> it = path.getNodes().iterator();
if (it.hasNext()) // Any entries?
{
// The first entry in the path is always the start node
// We don't want to run bindings on that entry (again)
it.next(); // skip first entry
while (it.hasNext())
{
Node node = it.next();
Log.debug("Executing Node: " + node);
_lifecycle.runBindings(node,appentry.app,this);//运行绑定
appentry.setLifeCycleNode(node);
}
}
}
catch (Throwable t)
{
Log.warn("Unable to reach node goal: " + nodeName,t);
}
}
}
public abstract class ScanningAppProvider extends AbstractLifeCycle implements AppProvider
{
......
protected void doStart() throws Exception
{
......
File scandir = _monitoredDir.getFile();
Log.info("Deployment monitor " + scandir + " at interval " + _scanInterval);
_scanner = new Scanner();
_scanner.setScanDirs(Collections.singletonList(scandir));//设置要扫描的目录
_scanner.setScanInterval(_scanInterval);//设置扫描间隔时间
_scanner.setRecursive(_recursive);
_scanner.setFilenameFilter(_filenameFilter);//设置文件/目录过滤器
_scanner.setReportDirs(true);
_scanner.addListener(_scannerListener);//设置扫描监听器,当文件/目录被新增/删除/修改时触发
_scanner.start();
}
......
//应用被修改时触发的动作
protected void fileChanged(String filename) throws Exception
{
if (Log.isDebugEnabled()) Log.debug("changed ",filename);
App app = _appMap.remove(filename);//先删除
if (app != null)
{
_deploymentManager.removeApp(app);
}
app = ScanningAppProvider.this.createApp(filename);//再重新部署
if (app != null)
{
_appMap.put(filename,app);
_deploymentManager.addApp(app);
}
}
}
public class Scanner
{
......
public synchronized void start ()
{
if (_running)
return;
_running = true;
if (_reportExisting)
{
// if files exist at startup, report them
scan();
}
else
{
//just register the list of existing files and only report changes
scanFiles();
_prevScan.putAll(_currentScan);
}
schedule();//启动定时器
}
......
public synchronized void scan ()
{
scanFiles();
reportDifferences(_currentScan, _prevScan);//扫描出修改过的应用,包括 新增/删除/修改
_prevScan.clear();
_prevScan.putAll(_currentScan);//将当前结果存储起来,以和下一次扫描结果进行比对
}
}
四、下一步:
jetty 实现 servlet 容器原理分析
jetty Continuation 实现原理分析
分享到:
相关推荐
** Jetty热部署 ** Jetty是一款轻量级的Java Web服务器和Servlet容器,支持热部署,即在不重启服务器的情况下更新Web应用。在开发过程中,热部署可以显著提高开发效率。 1. **集成Jetty到Maven** - 添加Jetty ...
6. **热部署**:除了自动重新加载外,Maven Jetty Plugin还支持类热替换,这意味着在运行时可以更新已加载的类,而无需重启服务器。 7. **与其他Maven插件协同工作**:与其他Maven插件如Surefire、Failsafe等配合...
**JRebel for IDEA 2017:热部署详解** 在软件开发过程中,开发者经常需要频繁地修改代码并测试效果,传统的IDEA(IntelliJ IDEA)在修改代码后,需要重新编译、重启应用才能看到改动的效果,这极大地降低了开发...
- **动态热部署**:如何在运行时更新应用而无需重启Jetty。 - **Servlet2.5新特性**:讨论Jetty对Servlet 2.5规范的支持。 6. **Handler详解**:涵盖不同类型的Handler,如Request Handler、Session Handler等,...
与Tomcat相比,Jetty具有更好的性能表现和更小的内存占用,特别是在开发过程中能够实现热部署功能,即代码修改后无需重启服务器即可生效,大大提高了开发效率。 #### 二、Jetty插件的安装步骤 ##### 2.1 打开...
8. **Web应用部署**:书中详细阐述了静态部署和动态热部署的方法,同时涵盖了Servlet 2.5的新特性。 9. **Handler详解**:Handler是Jetty处理请求的核心,不同的Handler负责不同类型的处理,如请求路由、会话管理等...
除了静态部署外,Jetty还支持动态热部署,即在Jetty运行时动态地添加、删除或更新Web应用程序,无需重启服务器即可生效。 **5.4 Servlet 2.5新特性** Jetty支持Servlet 2.5规范,该规范引入了一些新特性,如Filter...
- **部署更新**: Jetty 支持热部署,可以在不中断服务的情况下更新应用程序。 ### 6. 社区与扩展 Jetty 拥有活跃的社区,提供了丰富的文档和示例。开发者可以根据需要扩展Jetty,例如编写自定义的Servlet容器、...
- **HSF_Jetty插件**:该插件解决了开发过程中频繁重启服务器的问题,通过将Jetty集成到IDE中,可以实现实时热更新。 - **问题与解决方案**: - **依赖类**:在开发过程中,依赖的类可能需要动态加载。通过IDE提供...
- 支持热部署:当源代码发生变化时,"run-jetty-run"可以自动重新加载应用,提高开发效率。 - 参数配置:允许开发者设置端口、上下文路径等参数,适应不同的项目需求。 4. **使用Run-Jetty-Run的步骤** - 首先,...
1. **DevTools**:Spring Boot DevTools是专为开发人员设计的一组工具,它可以在应用重启时快速加载更改过的类,实现了基本的热部署功能。 2. **JRebel**:JRebel是一款商业的热部署插件,它可以实时更新代码而无需...
- 部署:支持静态部署、动态热部署以及Servlet 2.5及以上新特性。 6. **Jetty 架构** - Connector:负责网络通信,如HTTP连接的处理。 - Handler:处理HTTP请求的核心组件,可以自定义Handler实现特定功能。 - ...
- **动态热部署**: 允许在运行时动态添加、移除和更新Web应用程序。 #### 六、Handler详细介绍 - **Handler接口**: 定义了处理请求的基本方法。 - **具体实现**: 包括`ContextHandler`、`ResourceHandler`等。 - **...
2. **容器支持**:像Tomcat、Jetty这样的应用服务器提供了热部署功能,可以在不重启服务器的情况下更新Web应用程序。 3. **开发工具**:Eclipse、IntelliJ IDEA等IDE提供了热部署插件,使得开发者可以实时看到代码...
在IDE中,往往有集成的Maven支持,可以直接运行jetty:run目标,实现热部署,方便调试。 5. 整合与测试:开发者可以编写JUnit测试用例来验证Spring MVC的控制器和服务功能是否正常工作。由于Maven支持测试框架,所以...
JRebel 是一款高效的应用热部署工具,由 Zeroturnaround 公司开发,专为Java开发者设计,它极大地提升了开发效率,允许开发者在不重启应用服务器的情况下实时看到代码变动。在2018年版本中,JRebel 对 IntelliJ IDEA...
Jetty支持热部署,可以在不中断服务的情况下更新应用。 2. **配置文件**:Jetty的配置主要通过XML文件进行,包括服务器的端口设置、连接器类型、会话管理等。 3. **嵌入式使用**:Jetty可以被嵌入到其他Java应用中...
在JFinal的Maven项目中,可以使用Jetty插件实现在开发过程中快速启动和调试Web应用,无需部署到正式服务器。具体步骤如下: 1. **添加Jetty插件依赖**:在Maven的POM.xml文件中引入Jetty插件,并配置相关参数。 2. ...
为了解决这一问题,Zeroturnaround公司推出了JRebel插件,它能够实现在不重启应用的情况下,实时热部署代码更改,从而显著提升开发速度。 **JRebel是什么?** JRebel是一款强大的Java应用热部署工具,它通过动态...