探秘Tomcat——连接篇
前两篇我们分别粗线条和细粒度的讲解了tomcat的服务是如何启动以及连接器Connector和容器Container又分别是如何被启动的.
本篇我们主要侧重tomcat中server、service以及connector和container之间是如何相互关联起来的。在此之前,我们分别看下这个类中的一些主要方法,尤其是用于相互关联绑定的方法。
Server:(Server代表了整个Catalina容器,一个server可以包含一个或多个Services)
1 getInfo //获取server的版本 2 getGlobalNamingResources 3 setGlobalNamingResources 4 getPort //返回监听关闭server的端口 5 setPort 6 getShutdown //返回关闭server的命令字符串比如"SHUTDOWN" 7 setShutdown 8 addService //在该server上添加一个service 9 await //一直监听,直到出现shutdown指令 10 findService //返回指定名称的service 11 findServices //返回所有在这个server上的services集合 12 removeService 13 initialize
Service:(Service是一组包含了一个Container和一个或多个Connector的集合)
1 getContainer //返回容器,该容器用于处理Service上的Connectors发送过来请求 2 setContainer 3 getInfo //返回Service的版本信息 4 getName //返回该Service的名字 5 setName 6 getServer //返回与此Service关联的Server,这与Server中的addService遥相呼应 7 setServer //绑定一个Server 8 addConnector //添加一个Connector 9 findConnectors //返回该Service上的所有Connector 10 removeConnector //删除指定的Connector,同时也以为该Connector与Container也解除联系 11 initialize 12 addExecutor //添加一个执行器 13 findExecutors 14 getExecutor 15 removeExecutor
Connector:(前面已经说过,一个Service中可以包含多个Container,但是只会有一个Connector,而Container有多层实现关系,并且有自己的实现规范,所以定义成了接口,而这里的Connector就是一个类而非接口)
1 Connector 2 Connector //构造函数,其中有设置Connector要用到的协议 3 getProperty //根据属性名,返回属性值 4 setProperty 5 getAttribute //也是根据属性名,返回属性值,但是getProperty返回的是String类型,这里是Object对象 6 setAttribute 7 removeProperty 8 getService //返回与之绑定的Service 9 setService //绑定Service 10 getAllowTrace 11 setAllowTrace //设置allowTrace,用于跟踪http的信息 12 isAvailable //判断是否可用于处理request,里面判断的标记是started,这意味着只有Connector启动了才能用于处理request 13 getBufferSize 14 setBufferSize 15 getContainer //返回当前Connector移交request的接收Container对象 16 setContainer 17 getEmptySessionPath 18 setEmptySessionPath 19 getEnableLookups 20 setEnableLookups 21 getInfo //返回Connector的版本信息 22 getMapper 23 getMaxHeaderCount //返回Container允许的最大headers个数 24 setMaxHeaderCount 25 getMaxParameterCount //返回GET和POST方法的最大个数 26 setMaxParameterCount 27 ... 28 getPort //返回监听request的端口 29 setPort 30 getProtocol //返回使用到的protocol handler,有Http/1.1和AJP/1.3 31 setProtocol 32 getProtocolHandlerClassName 33 setProtocolHandlerClassName 34 getProtocolHandler 35 getProxyName //设置代理的名字 36 setProxyName 37 getProxyPort 38 setProxyPort 39 getRedirectPort //重定向端口,如果 40 setRedirectPort 41 getScheme 42 setScheme 43 getSecure 44 setSecure 45 getURIEncoding //返回URI编码 46 setURIEncoding 47 getUseBodyEncodingForURI 48 setUseBodyEncodingForURI 49 getXpoweredBy 50 setXpoweredBy 51 setUseIPVHosts 52 getUseIPVHosts 53 getExecutorName 54 createRequest //创建或指派并返回Request对象,这里的Request有和Container关联 55 createResponse 56 addLifecycleListener 57 findLifecycleListeners 58 removeLifecycleListener 59 createObjectName 60 initialize //初始化Connector对象 61 pause 62 resume 63 start 64 stop 65 ... 66 init 67 destroy 68 toString
Container:(Container可以执行来自客户端的Request请求,并返回相应的Response)
getInfogetLoader //返回与此Container相关的Loader对象,如果没有Loader,则返回与其父Container关联的Loader setLoadergetLogger //返回Logger对象,用于打印log,同理如果当前没有Logger对象,则寻找父级LoggergetManager //返回Manager对象 setManagergetMappingObject //返回JMX对象名字 getObjectNamegetPipeline //返回与此Container相关的用于管理Valves的Pipeline getClustersetClustergetBackgroundProcessorDelaysetBackgroundProcessorDelaygetNamesetNamegetParent //返回父级Container setParentgetParentClassLoader //返回父级类加载器 setParentClassLoadergetRealmsetRealmgetResourcessetResourcesbackgroundProcessaddChild //添加一个子容器,在添加之前,需要在子容器中先调用setParent方法addContainerListener //添加事件监听器addPropertyChangeListener //添加属性值变化监听器 findChildfindChildrenfindContainerListenersinvoke //执行具体的Request,并得到具体的Response对象 removeChildremoveContainerListenerremovePropertyChangeListenerlogAccess
1.连接原理举例
首先我们在Catalina类的load方法中调用了方法createStartDigester,该方法在之前几篇有介绍过,主要是对于加载的server.xml文件中定义各个组件之间的关系。
比如方法中的片段:
digester.addObjectCreate("Server","org.apache.catalina.core.StandardServer","className"); digester.addSetProperties("Server"); digester.addSetNext("Server","setServer","org.apache.catalina.Server");
- addObjectCreate就是添加一个模式,当解析server.xml遇到Server的时候,就根据Server的className实例化一个Server对象,而默认实例化的类就是org.apache.catalina.core.StandardServer;
- addSetProperties用于设置Server的一些属性,具体属性在server.xml中有定义;
- addSetNext用于调用Server类的setServer方法,把当前Server添加进去。
再比如这几行代码:
digester.addObjectCreate("Server/Listener",null, // MUST be specified in the element"className"); digester.addSetProperties("Server/Listener"); digester.addSetNext("Server/Listener","addLifecycleListener","org.apache.catalina.LifecycleListener");
对应在server.xml中就是这几行
1 <!--APR library loader. Documentation at /docs/apr.html --> 2 <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" /> 3 <!--Initialize Jasper prior to webapps are loaded. Documentation at /docs/jasper-howto.html --> 4 <Listener className="org.apache.catalina.core.JasperListener" /> 5 <!-- Prevent memory leaks due to use of particular java/javax APIs--> 6 <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" /> 7 <!-- JMX Support for the Tomcat server. Documentation at /docs/non-existent.html --> 8 <Listener className="org.apache.catalina.mbeans.ServerLifecycleListener" /> 9 <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
- 同理addObjectCreate说的是在Server下的Listeners,根据className创建listener对象;
- addSetProperties用于设置Server的一些属性,具体属性在server.xml中有定义;
- addSetNext用于调用Server类的addLifecycleLisntener方法,把server.xml中定义的5个监听器都实例化并添加到server上。
还有关于Server和Service之间关系的代码
digester.addObjectCreate("Server/Service","org.apache.catalina.core.StandardService","className"); digester.addSetProperties("Server/Service"); digester.addSetNext("Server/Service","addService","org.apache.catalina.Service");
通过这些代码我们很容易理解Server和Service之间的关联关系(后面会详细介绍)
除了Server和Service之间的从属关系,我们还可以看到Service和Connector之间的关系
digester.addRule("Server/Service/Connector",new ConnectorCreateRule()); digester.addRule("Server/Service/Connector", new SetAllPropertiesRule(new String[]{"executor"})); digester.addSetNext("Server/Service/Connector","addConnector","org.apache.catalina.connector.Connector");
同理这里也是在Service下调用addConnector添加Connector(后面会详细介绍)
2.Connector和Container以及Connector和Service何时连接?
我们从Catalina的load方法开始,当执行到load中的digester.parse(inputSource)时,即跳转到Digester类的parse方法中,之后开始解析server.xml中依次遇到的各个元素。
当遇到server元素的时候,在代码中方法的执行顺序为Catalina.load->Digester.parse->Digester.startElement
startElement方法如下:
1 public void startElement(String namespaceURI, String localName, 2 String qName, Attributes list) 3 throws SAXException { 4 boolean debug = log.isDebugEnabled(); 5 6 if (saxLog.isDebugEnabled()) { 7 saxLog.debug("startElement(" + namespaceURI + "," + localName + "," + 8 qName + ")"); 9 } 10 11 // Parse system properties 12 list = updateAttributes(list); 13 14 // Save the body text accumulated for our surrounding element 15 bodyTexts.push(bodyText); 16 if (debug) { 17 log.debug(" Pushing body text '" + bodyText.toString() + "'"); 18 } 19 bodyText = new StringBuffer(); 20 21 // the actual element name is either in localName or qName, depending 22 // on whether the parser is namespace aware 23 String name = localName; 24 if ((name == null) || (name.length() < 1)) { 25 name = qName; 26 } 27 28 // Compute the current matching rule 29 StringBuffer sb = new StringBuffer(match); 30 if (match.length() > 0) { 31 sb.append('/'); 32 } 33 sb.append(name); 34 match = sb.toString(); 35 if (debug) { 36 log.debug(" New match='" + match + "'"); 37 } 38 39 // Fire "begin" events for all relevant rules 40 List rules = getRules().match(namespaceURI, match); 41 matches.push(rules); 42 if ((rules != null) && (rules.size() > 0)) { 43 for (int i = 0; i < rules.size(); i++) { 44 try { 45 Rule rule = (Rule) rules.get(i); 46 if (debug) { 47 log.debug(" Fire begin() for " + rule); 48 } 49 rule.begin(namespaceURI, name, list); 50 } catch (Exception e) { 51 log.error("Begin event threw exception", e); 52 throw createSAXException(e); 53 } catch (Error e) { 54 log.error("Begin event threw error", e); 55 throw e; 56 } 57 } 58 } else { 59 if (debug) { 60 log.debug(" No rules found matching '" + match + "'."); 61 } 62 } 63 64 }
Digester.startElement
当执行到rule.begin(namespaceURI, name, list)这行的时候,通过调试信息可以看到该rule的className为org.apache.catalina.core.StandardServer,所以最终会进入StandardServer的构造函数中。
另外,当解析server.xml到5个listener的时候,就会调用StandardServer的addLifecycleListener分别将这5个监听器实例化并添加到server上。
继续解析,当解析到
igester.addSetNext("Server/Service/Connector","addConnector","org.apache.catalina.connector.Connector");
的时候就会跳转到StandardService的addConnector方法中
1 public void addConnector(Connector connector) { 2 3 synchronized (connectors) { 4 connector.setContainer(this.container); 5 connector.setService(this); 6 Connector results[] = new Connector[connectors.length + 1]; 7 System.arraycopy(connectors, 0, results, 0, connectors.length); 8 results[connectors.length] = connector; 9 connectors = results; 10 11 if (initialized) { 12 try { 13 connector.initialize(); 14 } catch (LifecycleException e) { 15 log.error(sm.getString( 16 "standardService.connector.initFailed", 17 connector), e); 18 } 19 } 20 21 if (started && (connector instanceof Lifecycle)) { 22 try { 23 ((Lifecycle) connector).start(); 24 } catch (LifecycleException e) { 25 log.error(sm.getString( 26 "standardService.connector.startFailed", 27 connector), e); 28 } 29 } 30 31 // Report this property change to interested listeners 32 support.firePropertyChange("connector", null, connector); 33 } 34 35 }
首先解析到的是server.xml中的这个connector
<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />
下面的两行代码交代了一个Connector是如何关联上Container和Service的:
- connector.setContainer(this.container):说明了connector和container是如何关联的,调用connector对象的setContainer方法,而传进的值为this.Container,也就是当前StandardService的Container对象,这样就完成了Connector和Container之间的连接
- connector.setService(this):对外这里绑定了当前的StandardService作为其从属的service。
3.Service和Container是何时连接的?
继续解析直到Catalina.createStartDigester定义的
digester.addRuleSet(new HostRuleSet("Server/Service/Engine/"));
这时候会调用StandardService的setContainer方法:
1 public void setContainer(Container container) { 2 3 Container oldContainer = this.container; 4 if ((oldContainer != null) && (oldContainer instanceof Engine)) 5 ((Engine) oldContainer).setService(null); 6 this.container = container; 7 if ((this.container != null) && (this.container instanceof Engine)) 8 ((Engine) this.container).setService(this); 9 if (started && (this.container != null) && 10 (this.container instanceof Lifecycle)) { 11 try { 12 ((Lifecycle) this.container).start(); 13 } catch (LifecycleException e) { 14 ; 15 } 16 } 17 synchronized (connectors) { 18 for (int i = 0; i < connectors.length; i++) 19 connectors[i].setContainer(this.container); 20 } 21 if (started && (oldContainer != null) && 22 (oldContainer instanceof Lifecycle)) { 23 try { 24 ((Lifecycle) oldContainer).stop(); 25 } catch (LifecycleException e) { 26 ; 27 } 28 } 29 30 // Report this property change to interested listeners 31 support.firePropertyChange("container", oldContainer, this.container); 32 33 }
当执行到((Engine) this.container).setService(this);这里会跳转到StandardEngine的setService交代了container是如何绑定StandardService的。
并且在代码
1 synchronized (connectors) { 2 for (int i = 0; i < connectors.length; i++) 3 connectors[i].setContainer(this.container); 4 }
我们可以看到通过遍历所有的connector,将其与container绑定。
4.Server和Service又是何时连接的?
继续解析直到Catalina.createStartDigerster中的
digester.addSetNext("Server/Service","addService","org.apache.catalina.Service");
会调用StandardServer的addService方法
1 public void addService(Service service) { 2 3 service.setServer(this); 4 5 synchronized (services) { 6 Service results[] = new Service[services.length + 1]; 7 System.arraycopy(services, 0, results, 0, services.length); 8 results[services.length] = service; 9 services = results; 10 11 if (initialized) { 12 try { 13 service.initialize(); 14 } catch (LifecycleException e) { 15 log.error(e); 16 } 17 } 18 19 if (started && (service instanceof Lifecycle)) { 20 try { 21 ((Lifecycle) service).start(); 22 } catch (LifecycleException e) { 23 ; 24 } 25 } 26 27 // Report this property change to interested listeners 28 support.firePropertyChange("service", null, service); 29 } 30 31 }
service.setServer(this):该行会调用StandardService中的setServer为service绑定当前的StandardServer对象
5.小结
当server.xml中的rule解析完毕后,我们起码明白了:
- Server和Service是如何关联的;
- Service和Connector是如何关联的;
- Service和Container是如何关联的;
- Connector和Container是如何关联的
如果您觉得阅读本文对您有帮助,请点一下“推荐”按钮,您的“推荐”将是我最大的写作动力!如果您想持续关注我的文章,请扫描二维码,关注JackieZheng的微信公众号,我会将我的文章推送给您,并和您一起分享我日常阅读过的优质文章。
友情赞助
如果你觉得博主的文章对你那么一点小帮助,恰巧你又有想打赏博主的小冲动,那么事不宜迟,赶紧扫一扫,小额地赞助下,攒个奶粉钱,也是让博主有动力继续努力,写出更好的文章^^。
1. 支付宝 2. 微信
转载于:https://www.cnblogs.com/bigdataZJ/p/TomcatSourceZJ6.html
探秘Tomcat——连接篇相关推荐
- Tomcat 原理篇
TOMCAT 原理篇 一.Tomcat 组成(Tomcat 由以下组件组成) 1.server a) Server是一个Catalina Servlet容器: b) Server 可以包含一个或多个s ...
- TOMCAT 连接池数据库密码加密方法
分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow 也欢迎大家转载本篇文章.分享知识,造福人民,实现我们中华民族伟大复兴! 原文来自 ...
- tomcat连接池的配置与使用
今天做接jsp的作业,在页面跳转的时候一直遇到个问题,"org.apache.commons.dbcp.SQLNestedException: Cannot create JDBC driv ...
- 数据库连接池和Tomcat连接池的配置问题
在做系统优化的时候师哥给我们提了一个连接池的概念,问我们有没有配置,我对这个概念一无所知,于是进行了一些研究,连接池有很多,比如WCF.EF还有数据库.CAS也可以配连接池,这些连接池用通俗的语言来说 ...
- Tomcat连接池配置
1. Tomcat c3p0连接池配置 (1) 修改conf/server.xml,在<GlobalNamingResources>标签内添加如下内容 <Resource nam ...
- 探秘Tomcat(一)——Myeclipse中导入Tomcat源码
前言:有的时候自己不知道自己是井底之蛙,这并没有什么可怕的,因为你只要蜷缩在方寸之间的井里,无数次的生活轨迹无非最终归结还是一个圆形:但是可怕的是有一天你不得不从井里跳出来生活,需要重新审视井以外的生 ...
- tomcat连接mysql数据库_tomcat连接常用数据库的用法
一.用于数据库连接的术语: JDBC:(Java database connectivity)是基于java数据访问技术的一个API通过客户端访问服务器的数据库,是一个面向关系型数据库并提供一种方法查 ...
- 蓝牙耳机与电脑连接篇
蓝牙耳机怎样与电脑连接,这是很多蓝牙耳机用户比较关心的问题,今天编者kevin为大家带来新手上路之蓝牙耳机与电脑连接篇,让用户们充分感受到蓝牙耳机给我们生活带来的乐趣. 一般连接蓝牙耳机与电 ...
- java tomcat连接池,tomcat容器的内置连接池怎么使用(按时结帐)
tomcat容器的内置连接池怎么使用(按时结帐) tomcat容器的内置连接池怎么使用(按时结帐) 日期:2014-05-18 浏览次数:20325 次 tomcat容器的内置连接池如何使用(按时结帐 ...
最新文章
- JavaScript时间日期格式化
- outdated: 29.Blitter Function, RAW Texture Loading
- java中商业数据计算时用到的类BigDecimal和DecimalFormat
- 93号涨0.86元售6.2元/升 20日油价正式上调
- 阿里云Kubernetes服务上从零搭建GitLab+Jenkins+GitOps应用发布模型的实践全纪录
- centos 7 mysql 中文,解决centOS7 下mysql插入中文字符报错相关问题
- 想成为一个怎样的人?
- Java二十三设计模式之-----桥接模式
- mybatis 打印自定义完整日志
- 俄罗斯方块java代码_java的俄罗斯方块代码
- kis商贸系列加密服务器,金蝶KIS商贸系列V3.0发版说明教程.doc
- 恶意代码分析实战学习——静态分析
- 如何批量抠图换背景?这两个方法可以做到
- charles安装教程--以及针对安装证书后https<unknown>爬坑
- Oracle数据库管理每周一例-第十七期 ADG
- 指数分布c语言,C语言下泊松分布以及指数分布随机数生成器实现
- win10,win11后在cmd命令行输入python自动调用微软应用商店
- 本地Windows MIMIC III数据入库
- 麒麟v10服务器安装vnc
- Mysql的基本操作和用户权限----2021(ZSD版)