深入剖析TOMCAT
TOMCAT
容器分类
Engine:整个CatalinaServlet引擎
Host:包含一个或者多个Context容器的虚拟主机
Context:web应用容器,可以有多个wrapper
Wrapper:粒度最小的容器,没有子容器
相关组件:载入器,管道,映射器等
请求响应流程
HttpConnector -->serverSocket.accept()-->HttpConnector.start--HttpConnector.run-->createProcessor()--> HttpProcessor.process(socket)-->解析填充req,res()-->connector.getContainer().invoke(request, response)-->容器.invoke-->(new SimplePipelineValveContext()).invokeNext--> valves[subscript].invoke-->basic.invoke-->HttpProcessor.process(socket).finishResponse;
生命周期
Lifecyle接口
public void addLifecycleListener(LifecycleListener listener);
public LifecycleListener[] findLifecycleListeners();
public void removeLifecycleListener(LifecycleListener listener);
public void start() throws LifecycleException;
public void stop() throws LifecycleException;
LifecycleEvent
LifecycleListener
LifecycleSupport
容器实现Lifecyle接口 可以调用addLifecycleListener(LifecycleListener listener)增加监听器至LifecycleSupport.LifecycleListener[],容器重写start,stop方法实现逻辑(对容器,子容器进行启动,关闭),同时发送事件告知监听器,监听器实现具体逻辑。
((Lifecycle) context).addLifecycleListener(listener);
LifecycleSupport.fireLifecycleEvent(BEFORE_START_EVENT, null);
LifecycleListener.lifecycleEvent(LifecycleEvent event) {
日志收集
Logger<--LoggerBase
public static final int FATAL = Integer.MIN_VALUE;
public static final int ERROR = 1;
public static final int WARNING = 2;
public static final int INFORMATION = 3;
public static final int DEBUG = 4;
public Container getContainer();
public void setContainer(Container container);
public String getInfo();
public int getVerbosity();
public void setVerbosity(int verbosity);
public void addPropertyChangeListener(PropertyChangeListener listener);
public void log(String message);
public void log(Exception exception, String msg);
public void log(String message, Throwable throwable);
public void log(String message, int verbosity);
public void log(String message, Throwable throwable, int verbosity);
public void removePropertyChangeListener(PropertyChangeListener listener);
FileLogger
SystemErrLogger
SystemOutLogger
载入器
loader
public ClassLoader getClassLoader();
public Container getContainer();
public void setContainer(Container container);
public DefaultContext getDefaultContext();
public void setDefaultContext(DefaultContext defaultContext);
public boolean getDelegate();
public void setDelegate(boolean delegate);
public String getInfo();
public boolean getReloadable();
public void setReloadable(boolean reloadable);
public void addPropertyChangeListener(PropertyChangeListener listener);
public void addRepository(String repository);
public String[] findRepositories();
public boolean modified();
public void removePropertyChangeListener(PropertyChangeListener listener);
Session管理
Request#getSession()-->this.doGetSession(true):session.getSession()--> context.getManager()-->manager.createSession(sessionId):manager.findSession(this.requestedSessionId)
安全性
Realm领域对象
public Container getContainer();
public void setContainer(Container container);
public String getInfo();
public void addPropertyChangeListener(PropertyChangeListener listener);
public Principal authenticate(String username, String credentials);
public Principal authenticate(String username, byte[] credentials);
public Principal authenticate(String username, String digest,
String nonce, String nc, String cnonce,
String qop, String realm,
String md5a2);
public Principal authenticate(X509Certificate certs[]);
public boolean hasRole(Principal principal, String role);
public void removePropertyChangeListener(PropertyChangeListener listener);
class GenericPrincipal implements Principal
public GenericPrincipal(Realm realm, String name, String password) {
this(realm, name, password, null);
}
public GenericPrincipal(Realm realm, String name, String password, List roles) {
super();
this.realm = realm;
this.name = name;
this.password = password;
if (roles != null) {
this.roles = new String[roles.size()];
this.roles = (String[]) roles.toArray(this.roles);
if (this.roles.length > 0)
Arrays.sort(this.roles);
}
}
LoginConfig
TOMCAT启动读取web.xml,如果包含Login-Config元素的配置,会创建loginconfig对象并设置对于属性加入验证阀
Authenticator
System.setProperty("catalina.base", System.getProperty("user.dir"));
Connector connector = new HttpConnector();
Wrapper wrapper1 = new SimpleWrapper();
wrapper1.setName("Primitive");
wrapper1.setServletClass("PrimitiveServlet");
Wrapper wrapper2 = new SimpleWrapper();
wrapper2.setName("Modern");
wrapper2.setServletClass("ModernServlet");
Context context = new StandardContext();
// StandardContext's start method adds a default mapper
context.setPath("/myApp");
context.setDocBase("myApp");
LifecycleListener listener = new SimpleContextConfig();
((Lifecycle) context).addLifecycleListener(listener);
context.addChild(wrapper1);
context.addChild(wrapper2);
// for simplicity, we don't add a valve, but you can add
// valves to context or wrapper just as you did in Chapter 6
Loader loader = new WebappLoader();
context.setLoader(loader);
// context.addServletMapping(pattern, name);
context.addServletMapping("/Primitive", "Primitive");
context.addServletMapping("/Modern", "Modern");
// add ContextConfig. This listener is important because it configures
// StandardContext (sets configured to true), otherwise StandardContext
// won't start
// add constraint
SecurityCollection securityCollection = new SecurityCollection();
securityCollection.addPattern("/");
securityCollection.addMethod("GET");
SecurityConstraint constraint = new SecurityConstraint();
constraint.addCollection(securityCollection);
constraint.addAuthRole("manager");
LoginConfig loginConfig = new LoginConfig();
loginConfig.setRealmName("Simple User Database Realm");
// add realm
Realm realm = new SimpleUserDatabaseRealm();
((SimpleUserDatabaseRealm) realm).createDatabase("conf/tomcat-users.xml");
context.setRealm(realm);
context.addConstraint(constraint);
context.setLoginConfig(loginConfig);
connector.setContainer(context);
try {
connector.initialize();
((Lifecycle) connector).start();
((Lifecycle) context).start();
// make the application wait until we press a key.
System.in.read();
((Lifecycle) context).stop();
}
catch (Exception e) {
e.printStackTrace();
}
StandardWrapper
singleThreadModel (STM)
servlet类可以继承STM接口,保证同一时间只有一个线程访问该servlet实例的service的方法,为了提高性能 servlet容器会创建多个servlet实例并发执行service,如service方法里面依赖外部资源,应该考虑可能引起同步问题
StandardWrapper
维护一个STM实例池 根据servlet完全限定名动态载入Servlet类,Servlet的service()是由StandardWrapperValue调用
public Servlet allocate() throws ServletException {
...
servlet.init(ServletConfig servletConfig)
}
ServletConfig
public interface ServletConfig {
String getServletName();
ServletContext getServletContext();
String getInitParameter(String var1);
Enumeration<String> getInitParameterNames();
}
StandardWrapperFacade
包装类
StandardWrapperValue
StandardWrapper实例的基础阀:
1.执行与该servlet关联的所以过滤阀
2.执行servlet实例的service方法
为完成上述操作StandardWrapperValue.invoke还会执行下面操作
1.servlet = wrapper.allocate();
2.ApplicationFilterChain filterChain =createFilterChain(request, servlet);
3.调用过滤链的doFilte()方法,其中包括servlet实例的service方法
4.释放过滤链
6.若该service类不会被使用到,调用wrapper.unload( 5.wrapper.deallocate()
);
ApplicationFilterChain implements FilterChain
public void doFilter(ServletRequest request, ServletResponse response)
throws IOException, ServletException {}
ApplicationFilterConfig implements FilterConfig
org.apache.catalina.core.StandardContext#start#filterStart#ApplicationFilterConfig(Context context, FilterDef filterDef)
public ApplicationFilterConfig(Context context, FilterDef filterDef)
throws ClassCastException, ClassNotFoundException,
IllegalAccessException, InstantiationException,
ServletException {
super();
this.context = context;
setFilterDef(filterDef);
}
FilterDef
StandardContext
...
Host 和 Engine
class StandardHost extends ContainerBase implements Deployer, Host{
}
public class StandardEngine extends ContainerBase implements Engine {}
服务器组件和服务组件
public interface Server {
public String getInfo();
public NamingResources getGlobalNamingResources();
public void setGlobalNamingResources
(NamingResources globalNamingResources);
public int getPort();
public void setPort(int port);
public String getShutdown();
public void setShutdown(String shutdown);
public void addService(Service service);
public void await();
public Service findService(String name);
public Service[] findServices();
public void removeService(Service service);
public void initialize()
throws LifecycleException;
}
public interface Service {
public Container getContainer();
public void setContainer(Container container);
public String getInfo();
public String getName();
public void setName(String name);
public Server getServer();
public void setServer(Server server);
public void addConnector(Connector connector);
public Connector[] findConnectors();
public void removeConnector(Connector connector);
public void initialize()
throws LifecycleException;
}
Digester库
通过编写servlet.XML配置Tomcat,web.xml配置servlet/jsp引用,TOMCAT解析web.xml,基于web.xml配置 context对象,Disester库提供了支持
public final class ContextConfig implements LifecycleListener {
private static Digester webDigester = createWebDigester();
lifecycleEvent(){
...
if (event.getType().equals(Lifecycle.START_EVENT))
start();
else if (event.getType().equals(Lifecycle.STOP_EVENT))
stop();
}
start() {
...
defaultConfig();//处理DefaultWebXml = "conf/web.xml"
applicationConfig();/WEB-INF/web.xml
}
}
关闭钩子
Runtime.getRuntime().addShutdownHook(Thread hook);
====================================================================
//tomcat的关闭钩子
protected class CatalinaShutdownHook extends Thread {
public void run() {
if (server != null) {
try {
((Lifecycle) server).stop();
} catch (LifecycleException e) {
System.out.println("Catalina.stop: " + e);
e.printStackTrace(System.out);
if (e.getThrowable() != null) {
System.out.println("----- Root Cause -----");
e.getThrowable().printStackTrace(System.out);
}
}
}
}
}
TOMCAT启动
Catalinna
(new Catalina()).process(args)
public void process(String args[]) {
setCatalinaHome();
setCatalinaBase();
try {
if (arguments(args))
execute();
} catch (Exception e) {
e.printStackTrace(System.out);
}
}
protected void execute() throws Exception {
if (starting)
start();
else if (stopping)
stop();
}
server.await();
Bootstrap
启动TOMCAT的入口类之一。运行startup.bat或者startup.sh文件上时候,实际上调用了该类的main()方法。main方法会创建3个类在入侵,并实例化Catalina类。然后他调用Catalina实例的process方法
HostConfig
public class HostConfig
implements LifecycleListener, Runnable {
public void lifecycleEvent(LifecycleEvent event) {
// Identify the host we are associated with
try {
host = (Host) event.getLifecycle();
if (host instanceof StandardHost) {
int hostDebug = ((StandardHost) host).getDebug();
if (hostDebug > this.debug) {
this.debug = hostDebug;
}
setDeployXML(((StandardHost) host).isDeployXML());
setLiveDeploy(((StandardHost) host).getLiveDeploy());
setUnpackWARs(((StandardHost) host).isUnpackWARs());
}
} catch (ClassCastException e) {
log(sm.getString("hostConfig.cce", event.getLifecycle()), e);
return;
}
// Process the event that has occurred
if (event.getType().equals(Lifecycle.START_EVENT))
start();
else if (event.getType().equals(Lifecycle.STOP_EVENT))
stop();
}
protected void start() {
if (debug >= 1)
log(sm.getString("hostConfig.start"));
if (host.getAutoDeploy()) {
deployApps();
}
if (isLiveDeploy()) {
//动态部署
threadStart();
}
}
protected void deployApps() {
if (!(host instanceof Deployer))
return;
if (debug >= 1)
log(sm.getString("hostConfig.deploying"));
File appBase = appBase();
if (!appBase.exists() || !appBase.isDirectory())
return;
String files[] = appBase.list();
//部署描述符
deployDescriptors(appBase, files);
//部署WAR
deployWARs(appBase, files);
//部署目录
deployDirectories(appBase, files);
}
}
Deployer 部署器接口
public interface Deployer {
public static final String PRE_INSTALL_EVENT = "pre-install";
public static final String INSTALL_EVENT = "install";
public static final String REMOVE_EVENT = "remove";
public String getName();
public void install(String contextPath, URL war) throws IOException;
public void install(URL config, URL war) throws IOException;
public Context findDeployedApp(String contextPath);
public String[] findDeployedApps();
public void remove(String contextPath) throws IOException;
public void start(String contextPath) throws IOException;
public void stop(String contextPath) throws IOException;
}
StandardHost
private Deployer deployer = new StandardHostDeployer(this);
StandardHostDeployer
start(String contextPath)
stop(String contextPath)
install(String contextPath, URL war)
install(URL config, URL war)
Manager应用程序的servlet类
管理已部署的应用程序
JMX管理资源
=============================================================================================