前面的文章分析了在tomcat中的container与pipeline的设计。。。我们知道Server是Service对象的容器,
而Service可以有多个connector对象,但是只能有一个container(一般就是engine对象)对象。。
所以分析各个不同的container对象的入口就在于service对象。。。而在tomcat中一般都是用StandardService这个类型。。。
先来看看一个初略的继承结构吧:
这里其实StandardService算是比较简单的了,首先它继承了LifecycleMBeanBase,这说明这个对象在启动之后将会被注册到JMX上去。。。同时它实现了service接口。。。这里就先来看看这个接口的定义吧:
package org.apache.catalina;
import org.apache.catalina.connector.Connector;
import org.apache.catalina.mapper.Mapper;
public interface Service extends Lifecycle {
public Container getContainer(); //获取拥有的container,service对象只拥有一个container
public void setContainer(Container container); //设置拥有的container
public String getName(); //service的名字
public void setName(String name);
public Server getServer(); //返回所属的server对象
public void setServer(Server server);
public ClassLoader getParentClassLoader(); //获取parentClassLoader
public void setParentClassLoader(ClassLoader parent);
public String getDomain(); //在jmx注册在哪个domain下面
public void addConnector(Connector connector); //添加一个connector
public Connector[] findConnectors(); //返回所有的connector
public void removeConnector(Connector connector); // 移除一个connecter
public void addExecutor(Executor ex); //添加一个executor
public Executor[] findExecutors(); //返回所有的executor
public Executor getExecutor(String name); //根据名字获取executor
public void removeExecutor(Executor ex); //移除一个executor
Mapper getMapper(); //用于请求的map
}
上面接口的定义其实也还挺简单的,无非首先拥有的container对象的管理,另外这里会管理容纳多个connector对象,同时还有executor对象,在以前分析connector部分的时候,我们知道在connector的创建的时候可以指定executor对象,这里指定的executor就是定义在servie对象里面的,当然如果没有指定的话,那么将会创建默认的executor、。、、、
同时这里还有一个非常重要的值得注意的东西,那就是mapper,他将会具体的负责请求的路由。。。
好啦,那么接下来来看看StandardService类型的属性的定义吧:
private String name = null;
private static final StringManager sm =
StringManager.getManager(Constants.Package);
private Server server = null; //所属的server对象
protected final PropertyChangeSupport support = new PropertyChangeSupport(this); //用于支持监听属性的修改
protected Connector connectors[] = new Connector[0]; //connector的数组
private final Object connectorsLock = new Object(); //当修改connector时候用到的锁
protected final ArrayList<Executor> executors = new ArrayList<>(); //所有定义的executor,connector可以用这个里面定义的executor
protected Container container = null; //关联的container
private ClassLoader parentClassLoader = null; //classLoader
protected final Mapper mapper = new Mapper(); //用于请求的map,host的map,warpper,context的map啥的这里具体每个属性是干嘛用的注释应该说的蛮清楚的吧。。。接下来来看看StandardService启动吧:
//启动
protected void startInternal() throws LifecycleException {
if(log.isInfoEnabled())
log.info(sm.getString("standardService.start.name", this.name));
setState(LifecycleState.STARTING);
// Start our defined Container first
if (container != null) {
synchronized (container) {
container.start(); //先启动container
}
}
synchronized (executors) {
for (Executor executor: executors) {
executor.start(); //启动所有的executor
}
}
mapperListener.start(); //启动mapperListener
// Start our defined Connectors second
synchronized (connectorsLock) {
for (Connector connector: connectors) {
try {
// If it has already failed, don't try and start it
if (connector.getState() != LifecycleState.FAILED) {
connector.start(); //启动所有的connector
}
} catch (Exception e) {
log.error(sm.getString(
"standardService.connector.startFailed",
connector), e);
}
}
}
}这里代码应该也挺简单的吧,首先设置当前组件的状态,然后启动拥有的container对象,其实一般都是engine,然后启动所有定义的executor对象,接着启动mapperListener,这个非常重要,待会分析
接下来就是启动connector,这样service就算是启动起来了。。。
嗯,其实service这部分真的很简单。。没啥说的。。。
还啦。。。接下来就来看看mapperListener的start干了啥吧。。。
先来看看MapperListener的比较初略的继承体系吧:
这里LifecycleMBeanBase的继承,说明对象的启动会被注册到jmx上面去。。。另外这里比较有意思的是实现了ContainerListener和LifecycleListener,说明既可以响应container的事件,例如添加child,添加valve啥的,同时还可以响应组件的状态事件。。。。
这里先来看看它的属性的定义吧:
private final Mapper mapper; //service的mapper,用于对请求进行路由
private final Service service; //所属的service对象
private static final StringManager sm =
StringManager.getManager(Constants.Package);
private final String domain = null; //注册在jmx哪个域名下面
public MapperListener(Mapper mapper, Service service) {
this.mapper = mapper;
this.service = service;
}
这里属性不多吧,具体的干啥用的都在注释后面贴了出来。。。
接下来来看看它的启动吧:
public void startInternal() throws LifecycleException {
setState(LifecycleState.STARTING);
// Find any components that have already been initialized since the
// MBean listener won't be notified as those components will have
// already registered their MBeans
findDefaultHost(); //获取当前engine默认的host
Engine engine = (Engine) service.getContainer(); //获取当前service的container,其实也就是engine
addListeners(engine); //为engine添加listener,将listener都设置为当前
Container[] conHosts = engine.findChildren(); //获取这个engine所有的host
for (Container conHost : conHosts) { //遍历engine下面的所有的host定义
Host host = (Host) conHost;
if (!LifecycleState.NEW.equals(host.getState())) {
// Registering the host will register the context and wrappers
registerHost(host); //登记host对象
}
}
}这个首先设置当前组件的状态,接着获取engine对象定义的默认的host的名字,看一段配置文件就知道了:
<Engine name="Catalina" defaultHost="localhost">每一个engine都可以指定默认使用的host对象,后面的就是设置为默认的host对象的名字。。
这里获取service对象拥有的container对象,其实就是engine对象,然后调用addListeners方法为其添加监听,最后再遍历当前engine对象的所有的host对象,调用registerHost方法注册他们。。
这里先来看看这个addListeners方法吧:
//为container对象添加lister,都设置为当前
private void addListeners(Container container) {
container.addContainerListener(this); //将这个container对象的container事件的监听设置为当前对象
container.addLifecycleListener(this); //设置当前对象来监听状态事件
for (Container child : container.findChildren()) { //遍历这个对象的所有的子container对象
addListeners(child); //同时将所有的子container的监听设置为当前对象
}
}这里其实递归的来设置container的监听,不光设置当前container对象,还要讲当前container对象的子container的监听都设置了。。这样就保证了当前service整个container的体系都设置了当前MapperListener为监听。。。
好啦,既然要设置监听,那么我们就来看看对于事件他将会做啥吧,首先来看看如何来响应container的事件吧:
//也就是有child加入,或者valve加入的时候会激活 的事件,这里会将child的listener也设置为当前
public void containerEvent(ContainerEvent event) {
if (Container.ADD_CHILD_EVENT.equals(event.getType())) { //如果是添加child的事件
Container child = (Container) event.getData();
addListeners(child); // 为这个child添加listener,将其的lifecycle和containerlistenre都指定为当前对象
// If child is started then it is too late for life-cycle listener
// to register the child so register it here
if (child.getState().isAvailable()) {
if (child instanceof Host) {
registerHost((Host) child); //如果这个对象是host,那么需要注册host
} else if (child instanceof Context) {
registerContext((Context) child); //如果是context那么需要登记
} else if (child instanceof Wrapper) { //如果是warpper,登记
registerWrapper((Wrapper) child);
}
}
} else if (Container.REMOVE_CHILD_EVENT.equals(event.getType())) {
Container child = (Container) event.getData();
removeListeners(child);
// No need to unregister - life-cycle listener will handle this when
// the child stops
} else if (Host.ADD_ALIAS_EVENT.equals(event.getType())) {
// Handle dynamically adding host aliases
mapper.addHostAlias(((Host) event.getSource()).getName(),
event.getData().toString());
} else if (Host.REMOVE_ALIAS_EVENT.equals(event.getType())) {
// Handle dynamically removing host aliases
mapper.removeHostAlias(event.getData().toString());
} else if (Wrapper.ADD_MAPPING_EVENT.equals(event.getType())) { //warper的mapping事件
// Handle dynamically adding wrappers
Wrapper wrapper = (Wrapper) event.getSource(); //获取当前warpper对象
Context context = (Context) wrapper.getParent(); //获取当前warpper所属的context
String contextPath = context.getPath(); // 获取context的path
if ("/".equals(contextPath)) { //如果是根
contextPath = "";
}
String version = ((Context) wrapper.getParent()).getWebappVersion();
String hostName = context.getParent().getName(); //获取host的名字
String wrapperName = wrapper.getName(); //获取warpper对象的名字
String mapping = (String) event.getData(); //要map的路径
boolean jspWildCard = ("jsp".equals(wrapperName)
&& mapping.endsWith("/*"));
mapper.addWrapper(hostName, contextPath, version, mapping, wrapper, //在engine的mapper上面注册
jspWildCard, context.isResourceOnlyServlet(wrapperName));
} else if (Wrapper.REMOVE_MAPPING_EVENT.equals(event.getType())) {
// Handle dynamically removing wrappers
Wrapper wrapper = (Wrapper) event.getSource();
String contextPath = ((Context) wrapper.getParent()).getPath();
if ("/".equals(contextPath)) {
contextPath = "";
}
String version = ((Context) wrapper.getParent()).getWebappVersion();
String hostName = wrapper.getParent().getParent().getName();
String mapping = (String) event.getData();
mapper.removeWrapper(hostName, contextPath, version, mapping);
} else if (Context.ADD_WELCOME_FILE_EVENT.equals(event.getType())) {
// Handle dynamically adding welcome files
Context context = (Context) event.getSource();
String hostName = context.getParent().getName();
String contextPath = context.getPath();
if ("/".equals(contextPath)) {
contextPath = "";
}
String welcomeFile = (String) event.getData();
mapper.addWelcomeFile(hostName, contextPath,
context.getWebappVersion(), welcomeFile);
} else if (Context.REMOVE_WELCOME_FILE_EVENT.equals(event.getType())) {
// Handle dynamically removing welcome files
Context context = (Context) event.getSource();
String hostName = context.getParent().getName();
String contextPath = context.getPath();
if ("/".equals(contextPath)) {
contextPath = "";
}
String welcomeFile = (String) event.getData();
mapper.removeWelcomeFile(hostName, contextPath,
context.getWebappVersion(), welcomeFile);
} else if (Context.CLEAR_WELCOME_FILES_EVENT.equals(event.getType())) {
// Handle dynamically clearing welcome files
Context context = (Context) event.getSource();
String hostName = context.getParent().getName();
String contextPath = context.getPath();
if ("/".equals(contextPath)) {
contextPath = "";
}
mapper.clearWelcomeFiles(hostName, contextPath,
context.getWebappVersion());
}
}
这里很重要吧,首先是对于ADD_CHILD_EVENT事件,如果这里就设对这个加入的子container对象的监听,然后根据不同的类型,进行相应的登记 。。。。。
其实到这里就对整个MapperListener的运行就算比较了解了,通过对container的监控,来进行整个请求的map处理。。。这里就来看看registerHost方法做了啥吧:
//登记host对象
private void registerHost(Host host) {
String[] aliases = host.findAliases(); //获取当前host的别名
mapper.addHost(host.getName(), aliases, host); // 将host与其名字对应起来
for (Container container : host.findChildren()) { //登记这个host下面的所有的context
if (container.getState().isAvailable()) {
registerContext((Context) container);
}
}
if(log.isDebugEnabled()) {
log.debug(sm.getString("mapperListener.registerHost",
host.getName(), domain, service));
}
}这个其实就是在service对象中的mapper中建立host的名字,别名与host对象的对象。。这样通过名字就能很快的索引到host了。。。。它用于干啥呢?嗯,其实就是对http请求的host字段进行路由。。。
好啦,接下来来看看registerContext方法,怎么登记context对象吧(其实一个context对象就对应着一个web应用程序)。。
//登记context对象,在里面要接着登记warrper
private void registerContext(Context context) {
String contextPath = context.getPath(); //获取context的path
if ("/".equals(contextPath)) {
contextPath = "";
}
Host host = (Host)context.getParent(); //这里获取的其实是host
WebResourceRoot resources = context.getResources(); //获取root
String[] welcomeFiles = context.findWelcomeFiles(); //获取welcomeFile
mapper.addContextVersion(host.getName(), host, contextPath,
context.getWebappVersion(), context, welcomeFiles, resources); //对添加context的map
for (Container container : context.findChildren()) {
registerWrapper((Wrapper) container); //登记所有的warpper对象
}
if(log.isDebugEnabled()) {
log.debug(sm.getString("mapperListener.registerContext",
contextPath, service));
}
}这里首先获获取了当前context的path,然后获取这个context所属的host对象,接着获取webroot的引用,以及首页啥的,然后将这些属性一起注册到service的mapper上面去,这样就能够很快的对http请求的context的进行定位了。。。最后解释登记Wrapper(这里可以先简单的理解为servlet的一层包装)了:
//登记warpper对象
private void registerWrapper(Wrapper wrapper) {
String wrapperName = wrapper.getName(); //获取名字
Context context = (Context) wrapper.getParent(); //获取所属的context对象
String contextPath = context.getPath(); //获取context的path
if ("/".equals(contextPath)) {
contextPath = "";
}
String version = ((Context) wrapper.getParent()).getWebappVersion(); //webapp的版本
String hostName = context.getParent().getName(); //
String[] mappings = wrapper.findMappings(); //获取这个warpper所有的mapping信息,毕竟一个servlet可能可以处理多个请求路径嘛
for (String mapping : mappings) { //根据mapping的信息,在service的mapper里面进行mapper
boolean jspWildCard = (wrapperName.equals("jsp")
&& mapping.endsWith("/*"));
mapper.addWrapper(hostName, contextPath, version, mapping, wrapper,
jspWildCard,
context.isResourceOnlyServlet(wrapperName));
}
if(log.isDebugEnabled()) {
log.debug(sm.getString("mapperListener.registerWrapper",
wrapperName, contextPath, service));
}
}这里其实跟上面的差不多,只不过注册的时候多了一些信息,到这里位置,host的索引,context索引,直到warpper(servlet)的索引就算都搞定了。。。
那么是不是整个http请求的路由也就差不多啦。。。也就差分析Mapper对象了,这个以后再说吧。。
这里整个MapperListener的工作也都差不多啦,当然也还有一些状态事件的相应,例如启动啥的,其实要做的事情也都差不多,无非是登记,或者基础mapper的登记。。。。
这样对于tomcat是如何维护请求的map信息也就比较的清楚了。。。。
本文深入探讨Tomcat的StandardService和MapperListener。StandardService作为Service的实现,管理Container、Connector和Executor。MapperListener是关键组件,负责请求路由。它监听Container事件,动态更新请求映射信息,确保HTTP请求能正确路由到对应的Host、Context和Wrapper。
1129

被折叠的 条评论
为什么被折叠?



