其他分享
首页 > 其他分享> > tomcat 请求处理过程 及 详解

tomcat 请求处理过程 及 详解

作者:互联网

war包里面 存的 是class文件 jar包也一样  那么 为啥 tomcat还需要打成war包呢

因为jar包有含义的 可能是别的项目的依赖  war包的话 说明是web项目 源码写死的 读war包

如上图所示 源码判断直接 以.war结尾的

 

Tomcat的部署方式:
在部署的时候Tomcat是并行部署项目
可以参照:https://www.cnblogs.com/honger/p/10362774.html
描述符部署
deployDescriptors(configBase,configBase.list())
从configBase部署发布XML描述文件

描述符部署  (节点部署)

 

 


war包部署
deployWARs(appBase, filteredAppPaths);
.war可以全部是class文件。其中web.xml也是可以删除的。
但是将其打包为.jar包放在Tomcat文件夹下是无法生效的,因为在Java中一般.jar包是一个依赖包而且源码中也有文件类型判断。
用.war包结尾的就被Tomcat视为一个项目。
在Tomcat的代码中有一个判断,只有当程序为.war结尾时才会去走以下的逻辑。
文件夹部署
deployDirectories(appBase, filteredAppPaths);
可以通过指定地址部署,不用复制到Tomcat下。
在部署Tomcat项目的时候可以在server.xml文件中的目录下定义一个节点。path代表应用名(访问的时候的名字,http的地址),docBase代表项目的地址
 

 

在下图中总共有四种servlet容器  所以说tomcat是servet容器

Tomcat是一个Servlet容器
public class MyServlet extends HttpServlet {
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
         response.getWriter().println("hello")
    }
}



在实现该功能的时候肯定要执行myServlet.doGet()方法,该方法是由Tomcat实现的。
处理请求的大致流程。

开始请求--->Request--->RequestFacade--->myServlet.doGet(RequestFacade) 
//Tomcat最后调用的是myServlet.service(RequestFacade)方法,在方法中使用doGet方法



Tomcat四大容器
Engine,Host,Context,Wrapper
Tomcat相当于一个虚拟主机:一个虚拟主机下可以部署多个项目。也可以说多个项目属于一个虚拟主机。
Engine—>Host—>Context—>Wrapper—>Servlet
Engine,Host,Context,Wrapper都是servlet容器。
Engine,Host都是集群管理器。
Wrapper:对servlet进行区分。类似于,有多个类包含servlet,每个类中有 多个servlet(List),在Wrapper层将每个类中的servlet封装在一起,Context层将所有类的servlet封装在一起(LIst)。
普通的servlet在Tomcat中是所有的请求线程共享一个实例。(可以理解为单例)

public class MyServlet extends HttpServlet {
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
         response.getWriter().println("hello")
    }
}



如果实现了SingleThreadModel接口。每个请求都单独有一个servlet实例。

public class MyServlet extends HttpServlet implements SingleThreadModel{
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
         response.getWriter().println("hello")
    }
}

如下的nameproject是属于name.com主机的。下面的所有name必须保证是同一个IP地址,这样才能保证Tomcat能接收到请求。
在进行Host配置的时候一定要加上项目路径。例如
Host的优点:

可以针对每一个Host进行不同的配置。比如:单独记录日志。
当同一个机器上有多个不同类型的项目时容易区分。
相互之间的影响减少,解耦。
server.xml

<Host name="name.com" appBase="webappss">
    <Contest path="/nameproject" docBase="D:\IDEA\spring" >
    <!-- 所有访问name.com的日志文件都放在localhost_access_log下 -->
    <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
        prefix="localhost_access_log" suffix=".txt"
        pattern="%h %l %u %t &quot;%r&quot; %s %b" />
</Host>

<Host name="baidu.com" appBase="webappss2">
</Host>

<Host name="localhost" appBase="webappss2">
</Host>

 

tomcat中管道与阀机制:

四个容器中 都有管道 管道(Pipeline)里面有阀 value就是阀

 管道实现了Pipeline接口


Pipeline接口如上图

getFirst:拿到第一个阀门

getValves:拿到一组阀门

addValve:添加一个阀门

getBasic:取一个默认的阀门

setBasic:设置一个默认的阀门

阀门实现了Valve接口。查看接口代码如下图:


管道任务

该章节旨在说明当连接器调用了servlet容器的invoke()方法后会发生什么事。然后,在对应小节中讨论org.apache.catalina包中的4个相关接口,Pipleline、Valve、ValveContext和Conntained。

管道包含该servlet容器将要调用的任务。一个阀表示一个具体的执行任务。在servlet容器的管道中,除了有一个基础阀,还可以添加任意数量的阀。阀的数量指的是额外添加的阀数量,即不包括基础阀。有意思的是,可以通过编辑Tomcat的配置文件(server.xml)来动态地添加阀。下图显示了一条管道及其阀。

 

 

如果你对servlet编程中的过滤器有所了解的话,那么应该不难想像管道和阀的工作机制。管道就像过滤器链一样,而阀则好似是过滤器。阀与过滤器类似,可以处理传递给它的request对象和response对象。当一个阀执行完成后,会调用下一个阀继续执行。基础阀总是最后一个执行的


public void addValve(Valve valve) {
    
        // Validate that we can add this Valve
        if (valve instanceof Contained)
            ((Contained) valve).setContainer(this.container);
 
        // Start the new component if necessary
        if (getState().isAvailable()) {
            if (valve instanceof Lifecycle) {
                try {
                    ((Lifecycle) valve).start();
                } catch (LifecycleException e) {
                    log.error("StandardPipeline.addValve: start: ", e);
                }
            }
        }
 
        // Add this Valve to the set associated with this Pipeline
        if (first == null) {
        	first = valve;
        	valve.setNext(basic);
        } else {
            Valve current = first;
            while (current != null) {
				if (current.getNext() == basic) {
					current.setNext(valve);
					valve.setNext(basic);
					break;
				}
				current = current.getNext();
			}
        }
        
        container.fireContainerEvent(Container.ADD_VALVE_EVENT, valve);

1.当管道中第一个阀门不存在,则将新加入的阀门设置为第一个,同时将基础阀门设置为最后的阀门
2.当管道存在第一个阀门时,则将新加入的阀门放在所有普通阀门的最后面(除去基础阀门),基础阀门永远存放在普通阀门最后面

 

 

一个servlet容器可以有一条管道。当调用了容器的invoke()方法后,容器将处理工作交由管道完成,而管道会调用其中的第一个阀开始处理。当第一个阀处理完后,它会调用后续的阀继续执行任务,直到管道中所有的阀都处理完

成。

connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);这里首先拿到的是StandardEngine容器相关的管道,所有阀门中最后执行了基础的阀门org.apache.catalina.coreStandardEngineValve,调用invoke方法。
 

 

 

 

总结:       tomcat中的StandardEngine,StandardHost,StandardContext,StandardWrapper四个组件容器中都包含了一个管道对象,每个管道对象都是继承org.apache.catalina.core.ContainerBase类,从该类的getPipeline方法中获取。每个容器关联的管道都有一个默认的阀门,设置默认的阀门

 

 

tcp socket和http的关系

tcpip和http都是协议是约定好的规范,他们位于网络5层模型的传输层(tcp)和应用层(http),tcpip表示的是一系列协议,不过与本题没有太大关系。规定好的协议总要操作系统实现了才能使用。 
而socket就是操作系统实现的,tcpip协议族的接口,用于创建一个套接字,可以理解为,通过网络读写的文件描述符,socket、bind、listen、accept、connect一系列都是操作系统提供的接口用于实现tcp协议相关的功能。socket是抽象出来的

 

举例

例如:A想要B左边口袋的糖,A和B通过tcp连接后,A发送给B:我想要你左边口袋的糖,这时进程B如果没和A做过任何约定,理解这句话是很困难的,想要理解就要和A提前做约定,比如:你发给我的话我会按以下格式理解:“动作:位置/资源”,那么约定好了后,A就必须发送“获取:左边口袋/糖”,B就可以理解这句话,返回给A糖了,但是互联网上数以亿计的节点,是不可能互相都两两约定好的,为了让大家都能顺畅的交流、http协议出世,他说:发送方都按我规定的格式说话,接收方都按我规定的格式理解,就互相都明白了不是?

HTTP

http连接=以http协议为通信协议的tcp连接
顺便说说长连接和短连接的问题tcp连接本身属性是不分长短的,是根据使用方式来决定的,举个例子:
有一把不会损坏的刀(tcp连接),你砍一个人(请求一次)就扔了(断开连接)就叫一次性砍刀(短连接),砍多个人(请求多次)扔了就叫多次性砍刀(长连接),这把刀本身的属性并不包含长短,只是根据使用方法产生了不同的叫法

总结:http连接=以http协议为通信协议的tcp连接http短连接=以http协议为通信协议的,请求一次就断开的tcp连接http长连接=以http协议为通信协议的,请求多次才断开的tcp连接(这个是为了通过减少建立tcp连接的次数达到节省两端资源的目的)

Socket

socket则是tcpip中运输层tcp和udp协议中的一个实现寻址的重要实现。具体来说,IP是用来定位网络上的一台计算机,那这台计算机上运行这好多服务怎么定位呢?答案就是socket。
Socket 的出现只是使得程序员更方便地使用 TCP/IP 协议栈而已,是对TCP/IP协议的抽象,从而形成了我们知道 的一些最基本的函数接口,比如 create、listen、connect、accept、send、read和 write 等

比较重要的

参考博文 ,点击这里
在TCP和UDP同属于传输层,共同架设在IP层(网络层)之上。而IP层主要负责的是在节点之间(End to End)的数据包传送,这里的节点是一台网络设备,比如计算机。因为IP层只负责把数据送到节点,而不能区分上面的不同应用,所以TCP和UDP协议在其基础上加入了端口的信息,端口于是标识的是一个节点上的一个应用。除了增加端口信息,UPD协议基本就没有对IP层的数据进行任何的处理了。而TCP协议还加入了更加复杂的传输控制,比如滑动的数据发送窗口(Slice Window),以及接收确认和重发机制,以达到数据的可靠传送。不管应用层看到的是怎样一个稳定的TCP数据流,下面传送的都是一个个的IP数据包,需要由TCP协议来进行数据重组。

TCP和Socket和HTTP协议之间的关系:

TCP:是传输层协议,是用于建立连接,表示可以说话。
Socket:socket就是操作系统实现的,tcpip协议族的接口,用于创建一个套接字,可以理解为,通过网络读写的文件描述符,socket、bind、listen、accept、connect一系列都是操作系统提供的接口用于实现tcp协议相关的功能。咱们日常是基于传输层暴露的接口(socket连接,监听,读写等)进行应用层开发
HTTP:是应用层协议:http协议建立在tcp协议之上,也就是说tcp协议可以让两个程序说话,而http协议定义了说话的规则。

 

tcp协议可以让两个进程通过三次握手建立稳定的通信信道,发送字节流,而http协议建立在tcp协议之上,也就是说tcp协议可以让两个程序说话,而http协议定义了说话的规则。

 

而tomcat如下所示调用的时候 数据先进入操作系统socket然后呢 进入endpoint 这时候 使用http协议里面tomcat定义的三种io如下:

endpoint提供不同的io模型  nio  jio (就是bio)      apr(阿帕奇自己封装的io模型  是nio) 然后把数据解析成request 请求头那些的解析  请求体不用解析  然后 在传递给容器

 

 

 

 

 

Tomcat通过Endpoint组件接收socket连接,接收到一个socket连接后会执行如下步骤

  1. 第一次从socket中获取数据到InputBuffer中,BIO对应的是InternalInputBuffer,父类是AbstractInputBuffer
  2. 然后基于InputBuffer进行解析数据
  3. 先解析请求行,把请求方法,请求uri,请求协议等封装到org.apache.coyote.Request对象中
  4. org.apache.coyote.Request中的属性都是MessageBytes类型,直接可以理解为字节类型,因为从socket中获取的数据都是字节,在解析过程中不用直接把字节转成字符串,并且MessageBytes虽然表示字节,但是它并不会真正的存储字节,还是使用ByteChunk基于InputBuffer中的字节数组来进行标记,标记字节数组中的哪个一个范围表示请求方法,哪个一个范围表示请求uri等等。
  5. 然后解析头,和解析请求行类似
  6. 解析完请求头后,就基于请求头来初始化一些参数,比如Connection是keepalive是close,比如是否有Content-length,并且对于的长度是多少等等,还包括当前请求在处理请求体时应该使用哪个InputFilter。
  7. 然后将请求交给容器
  8. 容器再将请求交给具体的servlet进行处理
  9. servlet在处理请求的过程中会利用response进行响应,返回数据给客户端,一个普通的响应过程会把数据先写入一个缓冲区,当调用flush,或者close方法时会把缓冲区中的内容发送给socet,下面有一篇单独的文章讲解tomcat响应请求过程
  10. servlet处理完请求后,先会检查是否需要把响应数据发送给socket
  11. 接着看当前请求的请求体是否处理结束,是否还有剩余数据,如果有剩余数据需要把这些数据处理掉,以便能够获取到下一个请求的数据
  12. 然后回到第一步开始处理下一个请求

标签:协议,http,请求,tomcat,阀门,tcp,详解,处理过程,servlet
来源: https://blog.csdn.net/xjk201/article/details/117000707