在上一章查看tomcat启动文件都干点啥---Catalina.java中说道了构造Server,,这次尝试着说一下Tomcat中Server的内容,首先看一下org.apache.catalina.Server接口中定义的方法:

  

  从这里至少可以看出Server中包含很多Service,通过实现如下接口添加一个新的Service到Services的集合中,或者从集合中删除指定的Service: 

public void addService(Service service);
public void removeService(Service service);

  通过实现如下接口来完成通过service的名称返回Service的操作:  

public Service findService(String name);

  通过实现如下接口来完成获取返回Server中所有Service的操作:  

public Service[] findServices();

  对于Server的网络内容的设置和获取通过如下方法,包括设置地址,端口:  

public int getPort();
public void setPort(int port);
public String getAddress();
public void setAddress(String address);

  获取和指定shotdown命令:

public String getShutdown();
public void setShutdown(String shutdown);

  获取和设置父类的加载器:  

public ClassLoader getParentClassLoader();
public void setParentClassLoader(ClassLoader parent);

  如果设置了Catalina,那么也提供获取和设置的方法:  

public Catalina getCatalina();
public void setCatalina(Catalina catalina);

  通过Server接口至少我们能够得出结论:Server中包含多个Service对象。

  结构如下:

  

  值得注意的是Server借口继承了Lifecycle接口,

public interface Server extends Lifecycle 

  Lifecycle 接口就是来控制Server极其组件的生命周期的,组件实现Lifecycle借口,就可以提供一致化的机制来启动和停止组件。下面看一下 Lifecycle的内容:

  首先是一些常量列表,小插曲,在Tomcat7.0.53中,tomcat在此处的注释有小问题,有兴趣的人可以看一下。  

   //组件初始化之前的事件
public static final String BEFORE_INIT_EVENT = "before_init";
//组件初始化之后的事件
public static final String AFTER_INIT_EVENT = "after_init";
//组件start的事件
public static final String START_EVENT = "start";
//组件start之前的事件
public static final String BEFORE_START_EVENT = "before_start";
//组件start之后的事件
public static final String AFTER_START_EVENT = "after_start";
//组件stop之后的事件
public static final String STOP_EVENT = "stop";
//组件stop之前的事件
public static final String BEFORE_STOP_EVENT = "before_stop";
//组件stop之后的事件
public static final String AFTER_STOP_EVENT = "after_stop";
//组件destrop之后的事件
public static final String AFTER_DESTROY_EVENT = "after_destroy";
//组件destrop之前的事件
public static final String BEFORE_DESTROY_EVENT = "before_destroy";
//组件periodic的事件
public static final String PERIODIC_EVENT = "periodic";

  下面就是Lifecycle接口定义的方法列表:

  

  既然Server中包含的主要对象就是Service,实现了Service就是对外提供服务了,下面在看一下Service的接口定义:

  

  看了定义的方法之后,很想逐一说明一下,可能会发现问题:

  在Service中添加或移除connector的方法:    

public void addConnector(Connector connector);
public void removeConnector(Connector connector);

 说明在每个Service中有多个Connector。

  在Service中添加或移除Executor的方法:   

public void addExecutor(Executor ex);
public void removeExecutor(Executor ex);

  返回所有Connector的方法:  

public Connector[] findConnectors();

  返回所有executor的方法:  

public Executor[] findExecutors();

  设置和获取Container的方法:  

public Container getContainer();
public void setContainer(Container container);

  获取和设置关联的Server对象的方法:  

public void setServer(Server server);
public Server getServer();

  给Service设置获取名称的方法:  

public void setName(String name);
public String getName();

  以上就是Service接口定义的主要方法,得出在Service中包含一个或多个Connector,包含一个或多个Executors和一个Container对象。接着上面的Server---Service图我们可以得出如下关系图:

            |---------Connector 

 Server----Service----|

             |----------Container

  

  由此可知在Tomcat中的两个重要的组件就是Connector和Container。下面我们着重看一下Connector和Container。

  Container的主要功能是执行从客户端接收的请求,然后给出回应。看一下Container接口定义的方法:

  添加,删除和获取一个子Container:  

public void addChild(Container child);
public void removeChild(Container child);
public Container findChild(String name);
public Container[] findChildren();

  对应的在Container中就应该有设置和获取父Container的方法:  

public void setParent(Container container);
public Container getParent();

  在Container中添加,移除和获取事件监听器:

public void addContainerListener(ContainerListener listener);
public void removeContainerListener(ContainerListener listener);
public ContainerListener[] findContainerListeners();

  在Container中添加,移除和获取属性变更监听器:  

public void addPropertyChangeListener(PropertyChangeListener listener);
public void removePropertyChangeListener(PropertyChangeListener listener);

  触发Container事件:

public void fireContainerEvent(String type, Object data);

  记录指向这个container的请求与响应的日志:  

public AccessLog getAccessLog();

  设置和获取作用在该container及其子container上的方法的延迟时间,单位秒:  

public void setBackgroundProcessorDelay(int delay);
public int getBackgroundProcessorDelay();

  设置和获取相关的集群:  

public void setCluster(Cluster cluster);
public Cluster getCluster();

  设置和获取Loadeer:  

public void setLoader(Loader loader);
public Loader getLoader();

  设置和获取负责管理该Container对应Session pool的Manager对象:  

public void setManager(Manager manager);
public Manager getManager();

  设置和获取Container的名字描述:  

public void setName(String name);
public String getName();

  设置和获取父类的ClassLoader:  

public void setParentClassLoader(ClassLoader parent);
public ClassLoader getParentClassLoader();

  获取Pipeline,负责管理该Container中的相关值:  

public Pipeline getPipeline();

  设置和获取Container的上下文资源:  

public void setResources(DirContext resources);
public DirContext getResources();

  设置和获取启动和停止children container的线程数,可以并行的启动和停止子container:  

public void setStartStopThreads(int startStopThreads);
public int getStartStopThreads();

  Connector类中的变量已经方法实现如下:

  代表一个Container的入口的变量:  

protected Adapter adapter = null;

  实现Servlet的API规则匹配的变量:  

protected Mapper mapper = new Mapper();

  是否允许Trace:  

protected boolean allowTrace = false;

  异步请求的超时时间:  

protected  long asyncTimeout = 10000;

  是否允许DNS查找的标记:  

protected boolean enableLookups = false;

  

  Mapper监听器:  

protected MapperListener mapperListener = new MapperListener(mapper, this);

  GET和POST方法中,Container解析的最大的参数个数限制(默认值为1000,当设置数值小于0时,表示没有限制):  

protected int maxParameterCount = 10000;

  Container接收POST方法传递的最大数据(默认值为2M):  

protected int maxPostSize = 2 * 1024 * 1024;

  在Container认证时候默认保存的最大数据:(默认值4K):  

  protected int maxSavePostSize = 4 * 1024;

  一系列以逗号分割的,application/x-www-form-urlencoded形式的方法请求体,以什么方式转化成方法的集合:  

protected String parseBodyMethods = "POST";

  通过parseBodyMethods方式确定的方法集合:  

protected HashSet<String> parseBodyMethodsSet;

  监听请求端口的数量:(默认值为-1):  

protected int port = -1;

  connector对象将请求重定向到那个Server:  

  protected String proxyName = null;

  connector对象请求重定向到server的哪个端口:  

   protected int proxyPort = 0;

  从no-ssl到ssl重定向端口:  

protected int redirectPort = 443;

  通过connector接收到的所有请求的请求方案:  

  protected String scheme = "http";

  是否给每个接收到的请求设置安全连接标记:  

protected boolean secure = false;

  一个String帮助对象:  

protected static final StringManager sm = StringManager.getManager(Constants.Package);

  关联的Service对象:  

protected Service service = null;

  URL编码:  

protected String URIEncoding = null;

  是否用body编码给URL编码:(不明白)  

protected boolean useBodyEncodingForURI = false;

  是否用IP绑定虚拟主机:  

protected boolean useIPVHosts = false;

  

  下面看一下Connector的构造函数:

  

  public Connector() {
this(null);
} public Connector(String protocol) {
setProtocol(protocol);
// Instantiate protocol handler
try {
Class<?> clazz = Class.forName(protocolHandlerClassName);
this.protocolHandler = (ProtocolHandler) clazz.newInstance();
} catch (Exception e) {
log.error(sm.getString(
"coyoteConnector.protocolHandlerInstantiationFailed"), e);
}
}
  Connector的构造函数中第一步是根据protocol名称HTTP/1.1,AJP/1.3或者protocol handler的类的全路径名称,下面是setProtocol方法的代码实现:  
public void setProtocol(String protocol) {

        if (AprLifecycleListener.isAprAvailable()) {
if ("HTTP/1.1".equals(protocol)) {
setProtocolHandlerClassName
("org.apache.coyote.http11.Http11AprProtocol");
} else if ("AJP/1.3".equals(protocol)) {
setProtocolHandlerClassName
("org.apache.coyote.ajp.AjpAprProtocol");
} else if (protocol != null) {
setProtocolHandlerClassName(protocol);
} else {
setProtocolHandlerClassName
("org.apache.coyote.http11.Http11AprProtocol");
}
} else {
if ("HTTP/1.1".equals(protocol)) {
setProtocolHandlerClassName
("org.apache.coyote.http11.Http11Protocol");
} else if ("AJP/1.3".equals(protocol)) {
setProtocolHandlerClassName
("org.apache.coyote.ajp.AjpProtocol");
} else if (protocol != null) {
setProtocolHandlerClassName(protocol);
}
}

  然后根据setProtocol方法设置的protocol handler进行实例化,在setProtocol方法中调用的setProtocolHandlerClassName方法,如下:

  public void setProtocolHandlerClassName(String protocolHandlerClassName) {

        this.protocolHandlerClassName = protocolHandlerClassName;

    }

  给connector的变量protocolHandlerClassName赋值,然后根据protocolHandlerClassName的值进行实例化。进而赋值给protocolHandler 变量。

  然后是方法createObjectNameKeyProperties,该方法的作用是将请求的address参数拼接成字符串,包括type,port。下面是代码实现:  

 protected String createObjectNameKeyProperties(String type) {

        Object addressObj = getProperty("address");

        StringBuilder sb = new StringBuilder("type=");
sb.append(type);
sb.append(",port=");
int port = getPort();
if (port > 0) {
sb.append(getPort());
} else {
sb.append("auto-");
sb.append(getProperty("nameIndex"));
}
String address = "";
if (addressObj instanceof InetAddress) {
address = ((InetAddress) addressObj).getHostAddress();
} else if (addressObj != null) {
address = addressObj.toString();
}
if (address.length() > 0) {
sb.append(",address=");
sb.append(ObjectName.quote(address));
}
return sb.toString();
}

  创建一个Request对象,Request是一个对Coyote Request的封装,Coyote 这个东西很奇怪,是狼的意思,也不知道为什么外国人喜欢用动物名来给一个技术命名,hadoop,hive,pig等,说Coyote其实是对Socket的一个封装,将Socket的请求和相应封装成一个个Request和Response,具体如何封装,都包涵什么信息等内容以后展开说明:

public Request createRequest() {
Request request = new Request();
request.setConnector(this);
return (request);
}

  创建一个Response对象:  

public Response createResponse() {
Response response = new Response();
response.setConnector(this);
return (response);
}

  这里面值得注意的地方就是在request和response中,都有setConnector方法,所有connector是request和response的一个属性。

  下面看方法destroyInternal,这个方法是在LifecycleMBeanBase类中定义的,用来销毁mapperListener,protocolHandler从Service中移除这个Connector对象,代码实现如下:  

 @Override
protected void destroyInternal() throws LifecycleException {
mapperListener.destroy(); try {
protocolHandler.destroy();
} catch (Exception e) {
throw new LifecycleException
(sm.getString
("coyoteConnector.protocolHandlerDestroyFailed"), e);
} if (getService() != null) {
getService().removeConnector(this);
} super.destroyInternal();
}

  设置和获取是否允许Trace方法的执行:  

public void setAllowTrace(boolean allowTrace) {

        this.allowTrace = allowTrace;
setProperty("allowTrace", String.valueOf(allowTrace)); } public boolean getAllowTrace() { return (this.allowTrace); }

  设置和获取异步请求的过期时间:  

 public void setAsyncTimeout(long asyncTimeout) {

        this.asyncTimeout= asyncTimeout;
setProperty("asyncTimeout", String.valueOf(asyncTimeout)); }
public long getAsyncTimeout() { return asyncTimeout; }

  配置和获取参数,参数这部分在前面的章节已经提到过了:

public void setAttribute(String name, Object value) {
setProperty(name, String.valueOf(value));
}
public Object getAttribute(String name) {
return getProperty(name);
}

  剩下的方法都是设置和获取前面定义的变量的值。

  

  Server的主要接口已经介绍完了,下面看一下一些关键类的实现:

  Server接口的标准实现是StandardServer类,同时StandServer也继承了LifecycleMBeanBase类,看一下StandardServer中几个重要方法的实现:

  

  找几个重要的方法说明一下:

  向保存Connector的数组中添加新的Connector对象的方法addConnector,代码实现如下:  

 public void addConnector(Connector connector) {

        synchronized (connectors) {
connector.setService(this);
Connector results[] = new Connector[connectors.length + 1];
System.arraycopy(connectors, 0, results, 0, connectors.length);
results[connectors.length] = connector;
connectors = results; if (getState().isAvailable()) {
try {
connector.start();
} catch (LifecycleException e) {
log.error(sm.getString(
"standardService.connector.startFailed",
connector), e);
}
} // Report this property change to interested listeners
support.firePropertyChange("connector", null, connector);
} }

  首先要把Connector和Serice做关联,connector.setService(this),然后将要添加的connector对象添加到保存Connector对象的数组中,此处使用数组,完全是处于效率的考虑。然后查看当前Server对象的状态,如果状态合法的话,那么启动添加的connector对象。然后在更改此Connector的状态。

  返回Connector集合:  

@Override
public Connector[] findConnectors() { return (connectors); }

  在Connector集合中移除connector:  

public void removeConnector(Connector connector) {

        synchronized (connectors) {
int j = -1;
for (int i = 0; i < connectors.length; i++) {
if (connector == connectors[i]) {
j = i;
break;
}
}
if (j < 0)
return;
if (connectors[j].getState().isAvailable()) {
try {
connectors[j].stop();
} catch (LifecycleException e) {
log.error(sm.getString(
"standardService.connector.stopFailed",
connectors[j]), e);
}
}
connector.setService(null);
int k = 0;
Connector results[] = new Connector[connectors.length - 1];
for (int i = 0; i < connectors.length; i++) {
if (i != j)
results[k++] = connectors[i];
}
connectors = results; // Report this property change to interested listeners
support.firePropertyChange("connector", connector, null);
} }

  首先遍历Connector集合,找到要移除的connector,如果指定的connector对象状态合法,那么调用该connector的stop方法,然后将指定的connector对象关联的Server置为null,剩下的内容就是整理移除connector对象的Connector集合。

  设置Container方法,该container对象处理Service中所有connector中的请求:  

public void setContainer(Container container) {

        Container oldContainer = this.container;
if ((oldContainer != null) && (oldContainer instanceof Engine))
((Engine) oldContainer).setService(null);
this.container = container;
if ((this.container != null) && (this.container instanceof Engine))
((Engine) this.container).setService(this);
if (getState().isAvailable() && (this.container != null)) {
try {
this.container.start();
} catch (LifecycleException e) {
// Ignore
}
}
if (getState().isAvailable() && (oldContainer != null)) {
try {
oldContainer.stop();
} catch (LifecycleException e) {
// Ignore
}
} // Report this property change to interested listeners
support.firePropertyChange("container", oldContainer, this.container); }

  首先是处理这个Server中原有的Container,原来可能有Container也有可能没有,所以要做判断,如果存在的话,解除和Service的关联,然后要处理新的container对象。关联Service,启动Container。

  由于Service中只有一个Container,所以没有移除Container方法,在设置的时候其实是完成了删除更新的操作。

  看一下startInternal方法:  

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();
}
} synchronized (executors) {
for (Executor executor: executors) {
executor.start();
}
} // Start our defined Connectors second
synchronized (connectors) {
for (Connector connector: connectors) {
try {
// If it has already failed, don't try and start it
if (connector.getState() != LifecycleState.FAILED) {
connector.start();
}
} catch (Exception e) {
log.error(sm.getString(
"standardService.connector.startFailed",
connector), e);
}
}
}
}

  该方法就是逐一启动Service中的组件,Container,Executor,Connector。

  stopInternal方法:  

 protected void stopInternal() throws LifecycleException {

        // Pause connectors first
synchronized (connectors) {
for (Connector connector: connectors) {
try {
connector.pause();
} catch (Exception e) {
log.error(sm.getString(
"standardService.connector.pauseFailed",
connector), e);
}
}
} if(log.isInfoEnabled())
log.info(sm.getString("standardService.stop.name", this.name));
setState(LifecycleState.STOPPING); // Stop our defined Container second
if (container != null) {
synchronized (container) {
container.stop();
}
} // Now stop the connectors
synchronized (connectors) {
for (Connector connector: connectors) {
if (!LifecycleState.STARTED.equals(
connector.getState())) {
// Connectors only need stopping if they are currently
// started. They may have failed to start or may have been
// stopped (e.g. via a JMX call)
continue;
}
try {
connector.stop();
} catch (Exception e) {
log.error(sm.getString(
"standardService.connector.stopFailed",
connector), e);
}
}
} synchronized (executors) {
for (Executor executor: executors) {
executor.stop();
}
}
}

  由这两个方法也能看出来Lifecycle对于个个组件生命周期的一致的生命周期的管理机制。

  其实最开始想用本章说一下如何构建Server,但是觉得还是有必要将Server中的内容展开说明一下,在说如果构建的话可能更好理解。所以就有了这个只是具有说明意义的一节。

  

  

查看tomcat启动文件都干点啥---server对象的更多相关文章

  1. 查看tomcat启动文件都干点啥---catalina.bat(转)

    在上一次查看tomcat启动文件都干点啥一文中,我们总结出,startup.bat文件的作用就是找到catalina.bat文件,然后把参数传递给它,在startup.bat中,调用catalina. ...

  2. 查看tomcat启动文件都干点啥---catalina.bat

    在上一次查看tomcat启动文件都干点啥一文中,我们总结出,startup.bat文件的作用就是找到catalina.bat文件,然后把参数传递给它,在startup.bat中,调用catalina. ...

  3. 查看tomcat启动文件都干点啥---Catalina.java

    在前一章查看tomcat启动文件都干点啥---Bootstrap.java中我们得出结论,在Bootstrap中通过反射调用Catalina类中的getServer,start,stop,stopSe ...

  4. 查看tomcat启动文件都干点啥---Bootstrap.java

    在上一章查看tomcat启动文件都干点啥---catalina.bat,说了在catalina.bat中都走了什么流程,最重要的是,我们得出了如下这段命令: _EXECJAVA=start " ...

  5. 查看tomcat启动文件都干点啥

    以下所写的都是基于Windows 操作系统,tomcat7.0版本.一直在使用tomcat但是老实说对于tomcat本身并没有一个系统的掌握,今天饶有兴致的随便看了看,做了一点笔记,写一点心得,我本人 ...

  6. Linux下如何查看tomcat是否启动、查看tomcat启动日志

    在Linux系统下,重启Tomcat使用命令的操作! 1.首先,进入Tomcat下的bin目录 cd /usr/local/tomcat/bin 使用Tomcat关闭命令 ./shutdown.sh ...

  7. Linux下如何查看tomcat是否启动、查看tomcat启动日志(转)

    在Linux系统下,重启Tomcat使用命令的操作! 1.首先,进入Tomcat下的bin目录 cd /usr/local/tomcat/bin 使用Tomcat关闭命令 ./shutdown.sh ...

  8. 【linux】linux上 查看tomcat日志文件

    1.查看实时日志文件 tail -f catalina.out 2.实时查看日志文件 最后n行 tail -n -f catalina.out 3.退出tail命令 ctrl + C 4.翻页查看 日 ...

  9. 如何查看tomcat启动异常日志详情

    我的电脑同时使用两个jdk版本,默认1.7,eclipse使用的是1.8,,由于项目启动时有加载类需要jdk1.8的包,1.7不支持.所以导致项目在eclipse直接能够跑,而在外面的tomcat跑是 ...

随机推荐

  1. jquery validate 使用示范

    最近应公司要求做了一个jquery的示例文件,包括:模态窗口怎么实现:jquery validate下的校验:怎么做图片特效:怎么实现异步操作:实现图片上传剪切效果等很多特效: 这里把jquery校验 ...

  2. HDU 4655 Cut Pieces 找规律+简单计数

    解法参考:http://blog.csdn.net/a601025382s/article/details/9840125 #include <cstdio> #include <c ...

  3. Mysql 查询—按位运算

    前言:虽说这是件小事儿,但本宝宝思前想后,还是为它留下一笔,嘿嘿.反正写博客不浪费纸和笔!好久没有开启我的逗比模式了,我亲爱的乖徒弟DBA,DBB,DBAA等,好久不见你们,遥祝幸福快乐+DB. 整个 ...

  4. poj 1035 纯正的字符串水

    Spell checker Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 22673   Accepted: 8258 De ...

  5. 哈夫曼树(C++优先队列的使用)

       给定n个权值作为n个叶子结点,构造一棵二叉树,若带权路径长度达到最小,称为哈夫曼树(Huffman Tree).哈夫曼树是带权路径长度最短的树,权值较大的结点离根较近.    构造 假设有n个权 ...

  6. kafka+windows+java+springboot中的配置

    1.百度kafka+zookeeper+windows配置 1.1  zookeeper配置 dataDir=/tmp/zookeeper # the port at which the client ...

  7. SQL Server 重新编译存储过程的方式有三种

    SQL Server 中,强制重新编译存储过程的方式有三种: sp_recompile 系统存储过程强制在下次执行存储过程时对其重新编译.具体方法是:从过程缓存中删除现有计划,强制在下次运行该过程时创 ...

  8. LACP学习笔记

    LACP学习笔记 来源: https://blog.csdn.net/zhengmx100/article/details/53893902 参考文档:download.h3c.com.cn/down ...

  9. 【转】百度统计js被劫持用来DDOS Github

    原文链接:http://drops.wooyun.org/papers/5398 今天中午刷着全国最大的信息安全从业人员同性交友社区zone.wooyun.org的时候,忽然浏览器每隔2秒就不断的弹窗 ...

  10. 《c程序设计语言》读书笔记-删除字符串中匹配的字符

    #include <stdio.h> #include <string.h> #define Num 10 int main() { int c,i,j = 0,m,n = 0 ...