余晖落尽暮晚霞,黄昏迟暮远山寻
本站
当前位置:网站首页 > 编程知识 > 正文

Tomcat:网络请求原理分析(Tomcat原理)

xiyangw 2022-11-24 16:40 53 浏览 0 评论

一、Http请求过程总览

浏览器请求

http://localhost/test/index.jsp


  1. 用户点击网页内容,请求被发送到本机端口8080,被在那里监听的Coyote HTTP/1.1 Connector获得。
  2. Connector将Request包装成ServletRequest给它所在的Service的Engine来处理,并等待Engine的回应。
  3. Engine获得请求localhost/test/index.jsp,匹配所有的虚拟主机Host。
  4. Engine匹配到名为localhost的Host(即使匹配不到也把请求交给该Host处理,因为该Host被定义为该Engine的默认主机),名为localhost的Host获得请求/test/index.jsp,匹配它所拥有的所有的Context。Host匹配到路径为/test的Context(如果匹配不到就把该请求交给路径名为""的Context去处理)。
  5. path="/test"的Context获得请求/index.jsp,在它的mapping table中寻找出对应的Servlet。Context匹配到URL PATTERN为*.jsp的Servlet,对应于JspServlet类。
  6. 构造HttpServletRequest对象和HttpServletResponse对象,作为参数调用JspServlet的doGet()或doPost().执行业务逻辑、数据存储等程序。
  7. Context把执行完之后的HttpServletResponse对象返回给Host。
  8. Host把HttpServletResponse对象返回给Engine。
  9. Engine把HttpServletResponse对象返回Connector,Connector把ServletResponse对象封装成Response。
  10. Connector把Response返回给客户Browser。

二、具体组件及源码分析

2.1 外部网络请求

2.1.1 Connector

用户在浏览器中输入一个URL地址之后浏览器将发起一个Http的请求,通过网络传输数据,请求到我们的Tomcat服务器中,Tomcat使用Connector接收Socket请求数据并通过Coyote链接器封装底层网络通信,为Catalina容器提供了统一的接口。


在Coyote中,Tomcat支持一下3种协议:

  1. HTTP/1.1协议: 目前最常用的访问协议
  2. AJP协议: Apache提供的一种协议,用于和Apache HTTP Server集成。
  3. HTTP/2.0协议: 下一代HTTP协议,自Tomcat8.5版本开始支持。

针对HTTP和AJP协议,Coyote又按照I/O方式分别提供了不同的方案。

  1. BIO: 同步阻塞I/O模式,数据的读取写入必须阻塞在一个线程内等待其完成,Tomcat早期网络请求的默认实现方式(自8.5版本开始,已经移除)
  2. NIO: 同步非阻塞的I/O模型(当前Tomcat的默认实现)
  3. NIO2: 异步非阻塞I/O模型
  4. APR: Apache可移植运行库。

2.1.2 NioEndpoint

我们以NioEndpoint为例,来看一下Tomcat是如何实现网络请求收发的。

/**
* Defaults to using HTTP/1.1 NIO implementation.
* 默认使用NIO实现的http/1.1协议
*/
public Connector() {
  this("HTTP/1.1");
}


public Connector(String protocol) {
    boolean apr = AprStatus.getUseAprConnector() && AprStatus.isInstanceCreated()
            && AprLifecycleListener.isAprAvailable();
    ProtocolHandler p = null;
    try {
        //根据指定的协议,创建不同的处理器
        p = ProtocolHandler.create(protocol, apr);
    } catch (Exception e) {
        log.error(sm.getString(
                "coyoteConnector.protocolHandlerInstantiationFailed"), e);
    }
    //省略部分代码...
}

我们发现Tomcat默认使用HTTP/1.1协议创建Http11NioProtocol,它默认使用NioEndpoint

public Http11NioProtocol() {
    super(new NioEndpoint());
}

当Connector执行init()的同时运行了protocolHandler.init()最终运行了NioEndpoint.init(),最终就是启动了一个ServerSocket并绑定监听端口。

protected void initServerSocket() throws Exception {
  //省略部分代码
  serverSock = ServerSocketChannel.open();
    socketProperties.setProperties(serverSock.socket());
    InetSocketAddress addr = new InetSocketAddress(getAddress(), getPortWithOffset());
    serverSock.bind(addr, getAcceptCount());
}

当Connector执行start()方法时,最终执行了:NioEndpoint.startInternal()方法。该方法内部启动了一个Poller线程,一个Acceptor线程。其中,Acceptor用于监听客户端连接,并将Socket连接放入待处理事件缓存池。Poller循环从待处理的事件缓存队列中拿到请求交给线程池处理。Poller将Socket请求包装成为:SocketProcessor对象,执行processor.process()方法,最终调用:getAdapter().service(request, response);这里的getAdapter即CoyoteAdapter,最终找到容器中管道方法的第一个阀门方法,发起调用,此时,网络请求正式进入我们容器中。

// Calling the container 调用容器
connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);

TIPS 如果想要详细了解Tomcat接收请求的流程图可以查看官网提供的资料

http://tomcat.apache.org/tomcat-9.0-doc/architecture/requestProcess/request-process.png

2.2 容器内部请求路径匹配

public void service(org.apache.coyote.Request req, org.apache.coyote.Response res)
        throws Exception {
        //将coyote链接器中的Request,Response转换为我们常用的HttpServletRequest,HttpServletResponse
    Request request = (Request) req.getNote(ADAPTER_NOTES);
    Response response = (Response) res.getNote(ADAPTER_NOTES);

    //省略部分代码...

        // 匹配请求路径,将当前request请求到匹配到的servlet上
        postParseSuccess = postParseRequest(req, request, res, response);
        if (postParseSuccess) {
            //check valves if we support async
            request.setAsyncSupported(
                    connector.getService().getContainer().getPipeline().isAsyncSupported());
            // Calling the container 调用容器
            connector.getService().getContainer().getPipeline().getFirst().invoke(
                    request, response);
     // 省略部分代码...
}

在Tomcat启动的时候 Service持有了一个Mapper对象,它借助MapperListener在应用启动的过程中,将Engine,Host,Context,Wrapper路径都搜集了起来。当一个请求过来的时候它会去当前 Service中去匹配,最终将请求执行到我们StandardWrapperValve。此时会创建ApplicationFilterChain过滤器链;通过调用过滤器内部方法internalDoFilter;最终调用:javax.servlet.http.HttpServlet.service()方法,请求到应用中!

三、本文小结

我们通过一个Http请求路径分析了Tomcat是使用Socket接收网络请求,使用Coyote转换我们的网络协议参数,包装为ServletRequest和ServletResponse,使用匹配请求路径的方式最终找到我们应用中的Servlet地址。至此,可以说我们对Tomcat的主要功能基本上已经了解的七七八八了,后续我们将会陆续的分享一些工作中常用的小技巧。


程序员的核心竞争力其实还是技术,因此对技术还是要不断的学习,关注 “IT巅峰技术” 公众号 ,该公众号内容定位:中高级开发、架构师、中层管理人员等中高端岗位服务的,除了技术交流外还有很多架构思想和实战案例。

作者是 《 消息中间件 RocketMQ 技术内幕》 一书作者,同时也是 “RocketMQ 上海社区”联合创始人,曾就职于拼多多、德邦等公司,现任上市快递公司架构负责人,主要负责开发框架的搭建、中间件相关技术的二次开发和运维管理、混合云及基础服务平台的建设。

相关推荐

数控系统常见术语详解,机加工人士必备资料
数控系统常见术语详解,机加工人士必备资料

增量编码器(Incrementpulsecoder)回转式位置测量元件,装于电动机轴或滚珠丝杠上,回转时发出等间隔脉冲表示位移量。由于没有记忆元件,故不能准...

2023-09-24 17:42 xiyangw

功、功率、扭矩的关系

功=功率×时间work=power×timeW=P×T功=力×距离work=force×lengthW=F×LP×T=F×LP=F×L/T=F×V(velocity)具体到电机输出轴上,圆...

Wi-Fi协议(802.11 )常见专业术语汇总
Wi-Fi协议(802.11 )常见专业术语汇总

Wi-Fi协议(802.11)常见专业术语汇总AP(Accesspoint的简称,即访问点,接入点):是一个无线网络中的特殊节点,通过这个节点,无线网络中的...

2023-09-24 17:41 xiyangw

不需要策略模式也能避免满屏if/else
不需要策略模式也能避免满屏if/else

满屏if/elsejava复制代码publicstaticvoidmain(String[]args){inta=1;if...

2023-09-24 17:41 xiyangw

喜极而泣,我终于干掉了该死的 if-else
喜极而泣,我终于干掉了该死的 if-else

推荐阅读:面试淘宝被Tomcat面到“自闭”,学习这份文档之后“吊打”面试官刷完spring+redis+负载均衡+netty+kafka面试题,再去面试BAT...

2023-09-24 17:40 xiyangw

Python中使用三元运算符简化if-else语句
Python中使用三元运算符简化if-else语句

Python是一种极简主义的编程语言,相比其他编程语言,在多个地方简化了代码的写法,可以让我们用更少的时间更简洁地完成工作。以赋值运算符为例:a=a+b简化...

2023-09-24 17:40 xiyangw

雅思课堂 | 雅思口语写作句型第二讲
雅思课堂 | 雅思口语写作句型第二讲

纯干货,无废话用最少的时间学最制胜的内容!泡图书馆泡不过学霸?碎片时间也能弯道超车!向着雅思8分行动起来吧!雅思口语写作句型1.Ipreferseeing...

2023-09-24 17:39 xiyangw

设计模式(三)——简单的状态模式代替if-else
设计模式(三)——简单的状态模式代替if-else

博主将会针对Java面试题写一组文章,包括J2ee,SQL,主流Web框架,中间件等面试过程中面试官经常问的问题,欢迎大家关注。一起学习,一起成长。前言大多数开...

2023-09-24 17:38 xiyangw

如何优化代码中大量的if/else,switch/case?

前言随着项目的迭代,代码中存在的分支判断可能会越来越多,当里面涉及到的逻辑比较复杂或者分支数量实在是多的难以维护的时候,我们就要考虑下,有办法能让这些代码变得更优雅吗?正文使用枚举这里我们简单的定义一...

优秀程序员早就学会用“状态模式”代替if-else了
优秀程序员早就学会用“状态模式”代替if-else了

2020年已经进入倒计时了,大家立好的flag完成了吗?2020实“鼠”不易,希望2021可以“牛”转乾坤。简介状态模式是行为型设计模式的一种。其设计理念是当对...

2023-09-24 17:37 xiyangw

用Select Case语句对执行多条件进行控制
用Select Case语句对执行多条件进行控制

今日的内容是"VBA之EXCEL应用"的第六章"条件判断语句(If...Then...Else)在VBA中的利用"。这讲是第三节...

2023-09-24 17:37 xiyangw

c#入门教程(四)条件判断if else

条件判断,是编程里常用的判断语句,比如某个代码如果满足条件就执行a代码块否则就执行b代码块。案例1:inti=2*5;if(a>0){执行a代码块}elseif(a<0){执行b代码块...

每日学编程之JAVA(十一)—条件语句(if……else)

一个if语句包含一个布尔表达式和一条或多条语句。如果布尔表达式的值为true,则执行if语句中的代码块,否则执行if语句块后面的代码。if语句后面可以跟else语句,当if语句...

不需要策略模式也能避免满屏if/else

除了使用策略模式以外,还可以使用其他设计模式来避免满屏if/else的问题。以下是一些可能的解决方案:工厂模式:将if/else语句移到工厂类中,由工厂类负责创建对象。这样可以将if/else语句从客...

围绕ifelse与业务逻辑的那些梗
围绕ifelse与业务逻辑的那些梗

ifelse很重要,几乎是程序员编程核心,业务逻辑与规则也通过ifelse体现出来,语句简单但是背后文章很大,先看几则幽默图:1.也许默认使用returnf...

2023-09-24 17:36 xiyangw

取消回复欢迎 发表评论: