Reloading Java Classes 301: Classloaders in Web Development — Tomcat, GlassFish, OSGi, Tapestry 5 an

The Original link : http://zeroturnaround.com/rebellabs/rjc301/

Copyright reserved by Rebel Inchtml

 

In this article we’ll review how dynamic classloaders are used in real servers, containers and frameworks to reload Java classes and applications.  We’ll also touch on how to get faster reloads and redeploys by using them in optimal ways.java

本章节咱们会对服务器, 容器, 框架如何动态使用动态classloader reload java类和应用程序作一个回顾.咱们也会说起如何使用优化的方法更快的reload和redeploy. web

Other Articles in the Reloading Java Classes Series


Java EE (web) applications

In order for a Java EE web application to run, it has to be packaged into an archive with a .WAR extension and deployed to a servlet container like Tomcat. This makes sense in production, as it gives you a simple way to assemble and deploy the application, but when developing that application you usually just want to edit the application’s files and see the changes in the browser.数据库

要想让一个Java EE web 应用跑起来, 他必须打包到一个.WAR包中并部署到一个servlet容器中(如Tomcat).这在生产环境中是颇有意义的, 由于他基于你一种很简单的装配和部署应用的方式, 可是当开发这样的应用的时候, 你一般都会想在编辑完程序文件后立刻在浏览器看到编辑完的改变.apache

A Java EE enterprise application has to be packaged into an archive with an .EAR extension and deployed to an application container. It can contain multiple web applications and EJB modules, so it often takes a while to assemble and deploy it. Recently, 1100+ EE developers told us how much time it takes them, and we compiled the results into the Redeploy and Restart Report.
Spoiler: Avg redeploy & restart time is 2.5 minutes – which is higher than we expected.小程序

一个Java EE企业级应用必须打包到.EAR中并部署到应用容器中. 他能够包含多个web应用和EJB模块, 因此一般他都会须要花一点时间才能装配和部署好. 最近, 1100+ 的EE 开发者告诉咱们这有多慢,  咱们把结果编辑到了 Redeploy and Restart Report.

In Reloading Java Classes 101, we examined how dynamic classloaders can be used to reload Java classes and applications. In this article we will take a look at how servers and frameworks use dynamic classloaders to speed up the development cycle. We’ll use Apache Tomcat as the primary example and comment when behavior differs in other containers (Tomcat is also directly relevant for JBoss and GlassFish as these containers embed Tomcat as the servlet container).windows

在 Reloading Java Classes 101, 咱们解释了动态classloaders如何用来reload java 类和应用. 在这篇文章中咱们探讨服务器和框架如何使用动态的classloader来加速开发.咱们会使用Apache Tomcat做为主要的例子, 并对它与其余容器的不懂行为进行比较(Tomcat也和JBoss和GlassFish直接相关, 由于他们是键入Tomcat做为servlet容器的)api

Redeployment浏览器

To make use of dynamic classloaders we must first create them. When deploying your application, the server will create one classloader for each application (and each application module in the case of an enterprise application). The classloaders form a hierarchy as illustrated:缓存

要使用动态classloader必须先建立他们.当部署你的应用时候, 服务器会为每一个应用建立一个classloader(一个企业级应用的每一个应用模块也会有一个classloader).这些classloader的层级关系以下:

classloaders-jee

In Tomcat each .WAR application is managed by an instance of the StandardContext class that creates an instance of WebappClassLoader used to load the web application classes. When a user presses “reload” in the Tomcat Manager the following will happen:

在tomcat中每一个.WAR 应用被一个StandardContext 类实例管理, 它建立了一个WebappClassLoader 实例来载入web应用的类. 当一个用户点击Tomcat Manager的"reload"实际上作了以下的事情:

  • StandardContext.reload() method is called
  • StandardContext.reload()方法被调用
  • The previous WebappClassLoader instance is replaced with a new one
  • 旧的WebappClassLoader会被新的代替
  • All reference to servlets are dropped
  • 全部对servlet的引用被丢弃
  • New servlets are created
  • 建立新的servlet
  • Servlet.init() is called on them
  • 新的servlet的Servlet.init()方法被调用

tomcat-cl-reload

Calling Servlet.init() recreates the “initialized” application state with the updated classes loaded using the new classloader instance. The main problem with this approach is that to recreate the “initialized” state we run the initialization from scratch, which usually includes loading and processing metadata/configuration, warming up caches, running all kinds of checks and so on. In a sufficiently large application this can take many minutes, but in a  in small application this often takes just a few seconds and is fast enough to seem instant, as commonly demonstrated in the Glassfish v3promotional demos.

调用Servlet.init()方法会使用新的classloader实例载入的新的classes来从新建立"初始化的"应用状态. 这种方法最主要的问题是从新建立"初始化的"状态须要从头作起, 这包括加载和处理元数据/配置, 预热缓存, 运行全部类型的检查等等. 在一个足够大的程序中, 这会花上不少时间, 但在小程序中这只会花上几秒钟, 足以让你立刻看到改变, 就像GlassFish v3 promotional demos 示范的那样.

If your application is deployed as an .EAR archive, many servers allow you to also redeploy each application module separately, when it is updated. This saves you the time you would otherwise spend waiting for non-updated modules to reinitialize after the redeployment.

若是你的文件是做为.EAR部署的, 许多服务器容许你在应用升级后将应用的模块分别重部署.这会节省不少你没必要要的等待没有升级的模块重部署后从新初始化的时间

Hot Deployment

Web containers commonly have a special directory (e.g. “webapps” in Tomcat, “deploy” in JBoss) that is periodically scanned for new web applications or changes to the existing ones. When the scanner detects that a deployed .WAR is updated, the scanner causes a redeploy to happen (in Tomcat it calls the StandardContext.reload() method). Since this happens without any additional action on the user’s side it is commonly referred to “Hot Deployment”.

Web容器一般有一个特殊的目录(Tomcat中的webapps, JBoss中的deploy), 用来对新的web应用或已经存在的应用的修改作按期扫描. 当扫描器检测到一个已经部署的.WAR更新了, 扫描器会触发一次重部署(Tomcat中是调用StandardContext.reload()方法). 因为这些操做的执行在用户端没有任何额外的操做, 因此被称为"热部署"

Hot Deployment is supported by all wide-spread application servers under different names: autodeployment, rapid deployment, autopublishing, hot reload, and so on. In some containers, instead of moving the archive to a predefined directory you can configure the server to monitor the archive at a specific path. Often the redeployment can be triggered from the IDE (e.g. when the user saves a file) thus reloading the application without any additional user involvement. Although the application is reloaded transparently to the user, it still takes the same amount of time as when hitting the “Reload” button in the admin console, so code changes are not immediately visible in the browser, for example.

热部署已经被大部分应用服务器以不一样名字支持: autodeployment, rapid depoyment, autopublishing, hot reload, 等待. 在一些容器内, 你能够指定服务器监控特定路径的archive, 而不是预约义的archive目录. 重部署一般能够经过IDE触发(例如当用户保存了一个文件是), 所以重载应用不要对用户有何额外的牵连. 虽然应用重加载对用户是透明的,但他其实在你点击"reload"按钮的时候会话大量时间来重载.因此代码的改变并不会立刻在浏览器中看到.

Another problem with redeployment in general and hot deployment in particular is classloader leaks. As we reviewed in Reloading Java Classes 201, it is amazingly easy to leak a classloader and quickly run out of heap causing an OutOfMemoryError. As each deployment creates new classloaders, it is common to run out of memory in just a few redeploys on a large enough application (whether in development or in production).

另外一个重部署和热部署的问题是classloader泄露.如咱们在Reloading Java Classes 201所讲, 泄露一个classloader是很容易的, 而且他会很快耗尽堆资源并形成 OutOfMemoryError. 由于每次部署都会生成新的classloader, 在一个大应用几回重部署后耗尽内存资源是很正常的(不管是在开发环境仍是生产环境)

Exploded Deployment

An additional feature supported by the majority of web containers is the so called “exploded deployment”, also known as “unpackaged” or “directory” deployment. Instead of deploying a .WAR archive, one can deploy a directory with exactly the same layout as the .WAR archive:

另一个web容器支持的特色叫作"分解部署", 也叫作"不打包"或"目录"部署.不部署一个WAR, 而是使用和war同样的结构部署一个目录

exploded

Why bother? Well, packaging an archive is an expensive operation, so deploying the directory can save quite a bit of time during build. Moreover, it is often possible to set up the project directory with exactly the same layout as the .WAR archive. This means an added benefit of editing files in place, instead of copying them to the server. Unfortunately, as Java classes cannot be reloaded without a redeploy, changing a .java file still means waiting for the application to reinitialize.

何须这样呢? 好吧, 打包一个archive是一个成本高昂的操做, 因此部署目录能够节省不少时间. 此外, 将项目目录结构设置成war那样也是颇有可能的事情. 这意味着适当的编辑文件有额外的好处, 而不是将他们复制到服务器.很不幸的是, java类不重部署是不能重加载的, 改变一个.java文件仍然表明须要等待程序从新初始化

With some servers it makes sense to find out exactly what triggers the hot redeploy in the exploded directory. Sometimes the redeploy will be triggered only when the “web.xml” timestamp changes, or as in the case of GlassFish only when a special ”.reload” file timestamp changes. In most servers any change to deployment descriptors or compiled classes will cause a hot redeploy.

对于某些服务器, 精确的找到在分解目录中是什么触发了热部署是颇有意义的. 有时候重部署只会在"web.xml"的时间戳被修改的时候被处罚, 在GlassFish的例子中, 只有当一个特殊的.reload文件被修改才会进行重部署. 大多数服务器中任何对部署描述符或编译类的修改都会形成热重部署.

If your server only supports deploying by copying to a special directory (e.g. Tomcat “webapps”, JBoss “deploy” directories) you can skip the copying by creating a symlink from that special directory to your project workspace. On Linux and Mac OS X you can use the common “ln -s” command to do that, whereas on Windows you should download the Sysinternals “junction” utility.

加入你的服务器只支持经过复制到特殊目录的方式重部署(例如, tomcat "webapps", JBoss "deploy" 文件夹), 你能够经过建立一个你的workspace到那个特殊目录的符号连接来跳过复制.在Linux和Max OS X中, 你可使用"ln -s"命令来作这个事情, 在windows中你则能够下载 Sysinternals "junction" utility.

If you use Maven, then it’s quite complicated to set up exploded development from your workspace. If you have a solo web application you can use the Maven Jetty plugin, which uses classes and resources directly from Maven source and target project directories. Unfortunately, the Maven Jetty plugin does not support deploying multiple web applications, EJB modules or EARs so in the latter case you’re stuck doing artifact builds.

若是你使用Maven, 那么在你的工做空间中设置搭建分解开发模式会至关复杂.若是你有一个单独的web项目你可使用Maven的Jetty插件, 它直接使用Maven源码和目标项目目录的classes和resources.

Session Persistence

Since we’re on the topic of reloading classes, and redeploying involves reinitializing an application, it makes sense to talk about session state. An HTTP session usually holds information like login credentials and conversational state. Losing that session when developing a web application means spending time logging in and browsing to the changes page – something that most web containers have tried to solve by serializing all of the objects in the HttpSession map and then deserializing them in the new classloader. Essentially, they copy all of the session state. This requires that all session attributes implement Serializable (ensuring session attributes can be written to a database or a file for later use), which is not restricting in most cases.

由于咱们讨论的是冲载入classes和重部署(包括从新初始化)一个应用, 那么谈一谈session的状态是颇有意义的.一个HTTP session一般会保存一些登陆凭证和会话状态.而在开发web应用时丢失session意味着须要花时间登陆并跳转到更改的页面 - 一些web容器尝试经过序列化全部HttpSession中的对象并在新的classloader中反序列化他们. 本质上他们复制了全部的session状态. 这须要全部的session参数都实现了Serializable接口(保证session参数能够被写入数据库或文件以便之后使用), 这在不少状况下都没法收到制约.

hot-deploy-session

Session persistence has been present in most major containers for many years (e.g. Restart Persistence in Tomcat), but was notoriously absent in Glassfish before v3.

Session持久化已经在大部分主流容器中共存在不少年了(例如 Tomcat的Restart Persistence), 但众所周知GlassFish在v3以前是没有的

OSGi

There is a lot of misunderstanding surrounding what exactly OSGi does and doesn’t do. If we ignore the aspects irrelevant to the current issue, OSGi is basically a collection of modules each wrapped in its own classloader, which can be dropped and recreated at will. When it’s recreated, the modules are reinitialized exactly the same way a web application is.

对于OSGi具体作了什么和没作什么事情有不少误解. 若是咱们忽略和这个问题相关的方面, OSGi基本上就是他本身的classloader的包装集合, 这是能够任意丢弃和从新建立的.

osgi

The difference between OSGi and a web container is that OSGi is something that is exposed to your application, that you use to split your application into arbitrarily small modules. Therefore, by design, these modules will likely be much smaller than the monolithic web applications we are used to building. And since each of these modules is smaller and we can “redeploy” them one-by-one, re-initialization takes less time. The time depends on how you design your application (and can still be significant).

 OSGi和web容器是OSGi是暴露给你的应用程序的, 你能够将你的程序切分红任意小的模块. 所以, 这些模块可能会远远小于咱们用来build的整个web应用程序.而因为每一个模块都很小,很显著的差别咱们以将他们一个接一个的重部署, 从新初始化花费的时间也会更少. 这个时间取决于你如何设计你的程序(这可能)

Tapestry 5, RIFE & Grails

Recently, some web frameworks, such as Tapestry 5, RIFE and Grails, have taken a different approach, taking advantage of the fact that they already need to maintain application state. They’ll ensure that state will be serializable, or otherwise easily re-creatable, so that after dropping a classloader, there is no need to reinitialize anything.

最近的一些框架, 例如 Tapestry 5, RIFE 和 Grails, 使用了一种不一样的方法, 这种方法已经能够用来维护程序的状态了. 他们保证状态能够被序列化, 或者其余简单的重建立, 因此在丢弃classloader后已经没有必要去从新初始化任何东西.

This means that application developers use frameworks’ components and the lifecycle of those components is handled by the framework. The framework will initialize (based on some configuration, either xml or annotation based), run and destroy the components.

这意味着程序开发者使用的框架组件和那些组件的生命周期都是被框架处理的.框架会初始化(基于某些配置, 不是xml就是注释), 运行和销毁这些组件

As the lifecycle of the components is managed by the framework, it is easy to recreate a component in a new classloader without user intervention and thus create the effect of reloading code. In the background, the old component is destroyed (classloader is dropped) and a new one created (in a new classloader where the classes are read in again) and the old state is either deserialized or created based on the configuration.

因为组件的什么周期由框架管理, 不须要用户接入就使用新的classloader重建一个组件是很简单的, 所以也能创造出重载代码的效果.在后台,旧的组件被销毁(classloader被丢弃)而新的被建立(在一个新的classloader再次载入该类), 旧的状态不是被反序列化就是基于配置被建立.

component

This has the obvious advantage of being very quick, as components are small and the classloaders are granular. Therefore the code is reloaded instantly, giving a smooth experience in developing the application. However such an approach is not always possible as it requires the component to be completely managed by the framework. It also leads to incompatibilities between the different class versions causing, among others, ClassCastExceptions.

这样的优点是很明显的, 因为组件很小而且classloader的粒度小, 他的速度能够很是快. 所以会被马上重载入,在程序开发过程当中能够提供流畅的体验.然而这样的方法并不老是有可能的, 由于他须要组件彻底被框架管理. 这也会致使不一样类版本的不兼容性, 在其余类中引起ClassCastException

We’ve Covered a Lot – and simplified along the way

It’s worth mentioning that using classloaders for code reloading really isn’t as smooth as we have described here – this is an introductory article series. Especially with the more granular approaches (such as frameworks that have per component classloaders, manual classloader dropping and recreating, etc), when you start getting a mixture of older and newer classes all hell can break loose. You can hold all kinds of references to old objects and classes, which will conflict with the newly loaded ones (a common problem is getting a ClassCastException), so watch what you’re doing along the way. As a side note: Groovy is actually somewhat better at handling this, as all calls through the Meta-Object Protocol are not subject to such problems.

值得一提的是使用classloader来进行代码重载并无咱们在这里提到的这么顺利 -- 这是一个介绍性的文章系列. 特别是随着一些更细粒度的方法(好比拥有每个组件的classloader的框架, 手动的丢弃和重建立classloader), 当你开始混合使用旧的和新的类的时候, 地狱之门可能已经打开了. 你能够持有全部旧的对象和类的引用, 这回跟新载入的形成冲突(常见问题是形成ClassCastException), 因此注意你正在作的事情. 附注: Groovy在这个问题上处理的更好一些, 由于全部经过Meta-Object协议的调用都没有受到这个问题的影响

This article addressed the following questions:

  • How are dynamic classloaders used to reload Java classes and applications?
  • 动态classloader是如何用来重载java类和应用的
  • How do Tomcat, GlassFish (incl v3), and other servers reload Java classes and applications?
  • Tomcat, GlassFish(包括v3),和其余服务器是如何重载java类和应用的
  • How does OSGi improve reload and redeploy times?
  • OSGi是如何改善重载和重部署的时间的
  • How do frameworks (incl Tapestry 5, RIFE, Grails) reload Java classes and applications?
  • 框架(包括Tapestry 5, RIFE, Grails)是如何重载类和应用的

Coming up next, we continue our explanation of classloaders and the redeploy process with an investigation into HotSwap and JRebel, two tools used to reduce time spent reloading and redeploying. Stay tuned!

接下来, 咱们会用HotSwap和JRebel继续对classloader和重部署过程做解释, 这两个工具用来减小重载和重部署的时间.请继续收看!

相关文章
相关标签/搜索