设为首页 收藏本站
查看: 1047|回复: 0

[经验分享] 查看tomcat启动文件都干点啥---server对象

[复制链接]
累计签到:4 天
连续签到:1 天
发表于 2015-8-6 11:35:49 | 显示全部楼层 |阅读模式
  在上一章查看tomcat启动文件都干点啥---Catalina.java中说道了构造Server,,这次尝试着说一下Tomcat中Server的内容,首先看一下org.apache.catalina.Server接口中定义的方法:
DSC0000.jpg
  从这里至少可以看出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对象。
  结构如下:
DSC0001.jpg
  值得注意的是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接口定义的方法列表:
DSC0002.png
  既然Server中包含的主要对象就是Service,实现了Service就是对外提供服务了,下面在看一下Service的接口定义:
DSC0003.jpg
  看了定义的方法之后,很想逐一说明一下,可能会发现问题:
  在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
DSC0004.jpg
  由此可知在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 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方法的代码实现:  

DSC0005.gif DSC0006.gif


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);
}
}
View Code  然后根据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();
}
View Code  创建一个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);
}
View Code    创建一个Response对象:  





public Response createResponse() {
Response response = new Response();
response.setConnector(this);
return (response);
}
View Code  这里面值得注意的地方就是在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();
}
View Code  设置和获取是否允许Trace方法的执行:  





public void setAllowTrace(boolean allowTrace) {
this.allowTrace = allowTrace;
setProperty("allowTrace", String.valueOf(allowTrace));
}
public boolean getAllowTrace() {
return (this.allowTrace);
}
View Code  设置和获取异步请求的过期时间:  





public void setAsyncTimeout(long asyncTimeout) {
this.asyncTimeout= asyncTimeout;
setProperty("asyncTimeout", String.valueOf(asyncTimeout));
}
public long getAsyncTimeout() {
return asyncTimeout;
}
View Code  配置和获取参数,参数这部分在前面的章节已经提到过了:





public void setAttribute(String name, Object value) {
setProperty(name, String.valueOf(value));
}
public Object getAttribute(String name) {
return getProperty(name);
}
View Code  剩下的方法都是设置和获取前面定义的变量的值。
  
  Server的主要接口已经介绍完了,下面看一下一些关键类的实现:
  Server接口的标准实现是StandardServer类,同时StandServer也继承了LifecycleMBeanBase类,看一下StandardServer中几个重要方法的实现:
DSC0007.png
  找几个重要的方法说明一下:
  向保存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);
}
}
View Code  首先要把Connector和Serice做关联,connector.setService(this),然后将要添加的connector对象添加到保存Connector对象的数组中,此处使用数组,完全是处于效率的考虑。然后查看当前Server对象的状态,如果状态合法的话,那么启动添加的connector对象。然后在更改此Connector的状态。
  返回Connector集合:  





@Override
public Connector[] findConnectors() {
return (connectors);
}
View Code  在Connector集合中移除connector:  





public void removeConnector(Connector connector) {
synchronized (connectors) {
int j = -1;
for (int i = 0; i < connectors.length; i++) {
if (connector == connectors) {
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;
}
connectors = results;
// Report this property change to interested listeners
support.firePropertyChange("connector", connector, null);
}
}
View Code  首先遍历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);
}
View Code  首先是处理这个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);
}
}
}
}
View Code  该方法就是逐一启动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();
}
}
}
View Code  由这两个方法也能看出来Lifecycle对于个个组件生命周期的一致的生命周期的管理机制。
  其实最开始想用本章说一下如何构建Server,但是觉得还是有必要将Server中的内容展开说明一下,在说如果构建的话可能更好理解。所以就有了这个只是具有说明意义的一节。
  
  

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-94774-1-1.html 上篇帖子: Servlet学习之web服务器Tomcat 详解 下篇帖子: J2EE开发环境搭建(1)——安装JDK、Tomcat、Eclipse
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表