Tomcat源码分析(四)-Connector

Tomcat7.0源码分析——请求原理分析(上) 谈起Tomcat的诞生,最早可以追溯到1995年。近20年来,Tomcat始终是使用最广泛的Web服务器,由于其使用Java语言开发,所以广为Java程序员所熟悉。很多人早期的J2EE项目,由程序员自己实现Jsp页面或者Servlet接受请求,后来借助Struts1、Struts2、Spring等中间件后,实际也是利用Filter或者Servlet处理请求,大家肯定要问了,这些Servlet处理的请求来自哪里?Tomcat作为Web服务器是怎样将HTTP请求交给Servlet的呢? 阅读详情

一、Connector内部分析

         

 

                                                                   Connector的内部结构

      Connector的作用:底层使用Socket连接,接收请求并把请求封装成Request和Response。

      (Connector实现了Http协议和TCP/IP协议,Request和Response内部是由Http协议封装)

      (Connector在Catalina调用load方法创建,生命周期方法在Service中调用。)

       封装完后------》交给Container处理------》返回给Connector------》Connector使用Socket将结果返回给客户端。

      ProtocolHandler  包含三个组件-------》1、Endpoint(AbstractEndpoint)  处理底层Socket的网络连接 ----用来实现TCP/IP协议

                              Acceptor 用于监听请求,AsyncTimeout用于检查异步request的超时,Handler用于处理接收到的Socket.

                                                       -------》2、Processor  将Endpoint接收到的Endpoint封装成Request  ----实现HTTP协议

                                                       -------》3、Adapter   将Request封装成交给Container处理。 ----请求转到Servlet容器处理

 

 

Connector的构造方法:

org.apache.catalina.connector.connector类

   public Connector() {
        this(null);
    }

    public Connector(String protocol) {
        setProtocol(protocol);
        // Instantiate protocol handler
        ProtocolHandler p = null;
        try {
            Class<?> clazz = Class.forName(protocolHandlerClassName);
                                                   //创建ProtocolHandler对象实例 
            p = (ProtocolHandler) clazz.getDeclaredConstructor().newInstance();
        } catch (Exception e) {
            log.error(sm.getString(
                    "coyoteConnector.protocolHandlerInstantiationFailed"), e);
        } finally {
            this.protocolHandler = p;
        }

        if (!Globals.STRICT_SERVLET_COMPLIANCE) {
            URIEncoding = "UTF-8";
            URIEncodingLower = URIEncoding.toLowerCase(Locale.ENGLISH);
        }
    }

   
       //// setProtocol(protocol);方法
       /**
     * Set the Coyote protocol which will be used by the connector.
     *
     * @param protocol The Coyote protocol name
     */
    public void setProtocol(String protocol) {
         //根据协议类型 设置protocolHandlerClassName
        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.Http11NioProtocol");
            } else if ("AJP/1.3".equals(protocol)) {
                setProtocolHandlerClassName
                    ("org.apache.coyote.ajp.AjpNioProtocol");
            } else if (protocol != null) {
                setProtocolHandlerClassName(protocol);
            }
        }

    }


      /**
     * Coyote Protocol handler class name.
     * Defaults to the Coyote HTTP/1.1 protocolHandler.
     */
    protected String protocolHandlerClassName =
        "org.apache.coyote.http11.Http11NioProtocol";

       /**
     * Set the class name of the Coyote protocol handler which will be used
     * by the connector.
     *
     * @param protocolHandlerClassName The new class name
     */
    public void setProtocolHandlerClassName(String protocolHandlerClassName) {

        this.protocolHandlerClassName = protocolHandlerClassName;

    }

    Connector(通过构造方法)创建对象实例的时候同时创建了ProtocolHandler对象实例。

    用 setProtocol(protocol); 设置好的protocolHandlerClassName的类(默认是org.apache.coyote.http11.Http11NioProtocol)

   然后用 Class<?> clazz = Class.forName(protocolHandlerClassName);创建ProtocolHandler对象

    上面代码中的 Apr是 Apache Portable Runtime的缩写 -->Apache可移植运行库

    主要为上层的应用程序提供一个可以跨越多操作系统平台使用的底层支持接口库。在早期 的Apache版本中,应用程序本身必须能够处理各种具体操作系统平台的细节,并针对不同的平台调用不同的处理函数。

    目前APR主要还是由Apache使用,不过由于APR的较好的移植性,因此一些需要进行移植的C程序也开始使用APR

    如果使用Apr需要先安装,安装后Tomcat可以检测的出来。

   AprLifecycleListener.isAprAvailable() 判断Apr是否可用。

 

  1.1 Connector的生命周期方法:


org.apache.catalina.connector.connector类:
 @Override
    protected void initInternal() throws LifecycleException {

        super.initInternal();

        // Initialize adapter
        adapter = new CoyoteAdapter(this);
        protocolHandler.setAdapter(adapter);

        // Make sure parseBodyMethodsSet has a default
        if( null == parseBodyMethodsSet ) {
            setParseBodyMethods(getParseBodyMethods());
        }

        if (protocolHandler.isAprRequired() &&
                !AprLifecycleListener.isAprAvailable()) {
            throw new LifecycleException(
                    sm.getString("coyoteConnector.protocolHandlerNoApr",
                            getProtocolHandlerClassName()));
        }

        try {
            protocolHandler.init();
        } catch (Exception e) {
            throw new LifecycleException
                (sm.getString
                 ("coyoteConnector.protocolHandlerInitializationFailed"), e);
        }
    }


    /**
     * Begin processing requests via this Connector.
     *
     * @exception LifecycleException if a fatal startup error occurs
     */
    @Override
    protected void startInternal() throws LifecycleException {

        // Validate settings before starting
        if (getPort() < 0) {
            throw new LifecycleException(sm.getString(
                    "coyoteConnector.invalidPort", Integer.valueOf(getPort())));
        }

        setState(LifecycleState.STARTING);

        try {
            protocolHandler.start();
        } catch (Exception e) {
            String errPrefix = "";
            if(this.service != null) {
                errPrefix += "service.getName(): \"" + this.service.getName() + "\"; ";
            }

            throw new LifecycleException
                (errPrefix + " " + sm.getString
                 ("coyoteConnector.protocolHandlerStartFailed"), e);
        }
    }


  @Override
    protected void destroyInternal() throws LifecycleException {
        try {
            protocolHandler.destroy();
        } catch (Exception e) {
            throw new LifecycleException
                (sm.getString
                 ("coyoteConnector.protocolHandlerDestroyFailed"), e);
        }

        if (getService() != null) {
            getService().removeConnector(this);      //service移除Connector
        }

        super.destroyInternal();
    }

   1.2 ProtocolHandler类:

ProtocolHandler下面有三种协议类型:AJP,Http,Spdy。

                        AJP协议-----》(英文全称Apache JServ Protocol),Apache的定向包协议,是长连接,用于客服端(前端服务器)建立                                                  连接 通信。

                       Spdy协议------》应用不广泛,SPDY(读作“SPeeDY”)是Google开发的基于TCP的应用                                                                                        层协议,用以最小化网络延迟,提升网络速度,优化用户的网络使用体验。SPDY并不是一种用于                                                  替代HTTP的协议,而是对HTTP协议的增强.

Http11NioProtocol的四个周期方法都在父类AbstractProtocol类实现--》 代码如下:

    
org.apache.coyote.AbstractProtocol类:
   /*
     * NOTE: There is no maintenance of state or checking for valid transitions
     * within this class. It is expected that the connector will maintain state
     * and prevent invalid state transitions.
     */

    @Override
    public void init() throws Exception {
        if (getLog().isInfoEnabled())
            getLog().info(sm.getString("abstractProtocolHandler.init",
                    getName()));

        if (oname == null) {
            // Component not pre-registered so register it
            oname = createObjectName();
            if (oname != null) {
                Registry.getRegistry(null, null).registerComponent(this, oname,
                    null);
            }
        }

        if (this.domain != null) {
            try {
                tpOname = new ObjectName(domain + ":" +
                        "type=ThreadPool,name=" + getName());
                Registry.getRegistry(null, null).registerComponent(endpoint,
                        tpOname, null);
            } catch (Exception e) {
                getLog().error(sm.getString(
                        "abstractProtocolHandler.mbeanRegistrationFailed",
                        tpOname, getName()), e);
            }
            rgOname=new ObjectName(domain +
                    ":type=GlobalRequestProcessor,name=" + getName());
            Registry.getRegistry(null, null).registerComponent(
                    getHandler().getGlobal(), rgOname, null );
        }

        String endpointName = getName();
        endpoint.setName(endpointName.substring(1, endpointName.length()-1));

        try {
            endpoint.init();
        } catch (Exception ex) {
            getLog().error(sm.getString("abstractProtocolHandler.initError",
                    getName()), ex);
            throw ex;
        }
    }


    @Override
    public void start() throws Exception {
        if (getLog().isInfoEnabled())
            getLog().info(sm.getString("abstractProtocolHandler.start",
                    getName()));
        try {
            endpoint.start();
        } catch (Exception ex) {
            getLog().error(sm.getString("abstractProtocolHandler.startError",
                    getName()), ex);
            throw ex;
        }
    }

 

 protocolHandlerClassName默认配置 --》org.apache.coyote.http11.Http11NioProtocol类:

/**
 * Abstract the protocol implementation, including threading, etc.
 * Processor is single threaded and specific to stream-based protocols,
 * will not fit Jk protocols like JNI.
 *
 * @author Remy Maucherat
 * @author Costin Manolache
 */
public class Http11NioProtocol extends AbstractHttp11JsseProtocol<NioChannel> {

    private static final Log log = LogFactory.getLog(Http11NioProtocol.class);


    @Override
    protected Log getLog() { return log; }


    @Override
    protected AbstractEndpoint.Handler getHandler() {
        return cHandler;
    }

 
    /**// Constants类的
     * // public static final int DEFAULT_CONNECTION_LINGER = -1;
     * // public static final int DEFAULT_CONNECTION_TIMEOUT = 60000;
     * // public static final boolean DEFAULT_TCP_NO_DELAY = true;
     **/
    public Http11NioProtocol() {
        endpoint=new NioEndpoint();                         //创建Endpoint
        cHandler = new Http11ConnectionHandler(this);      //创建Http11ConnectionHandler
        ((NioEndpoint) endpoint).setHandler(cHandler);
             //父类AbstractProtocol的endpoint.setSoLinger(soLinger);              
        setSoLinger(Constants.DEFAULT_CONNECTION_LINGER);
             //父类AbstractProtocol的endpoint.setSoTimeout(timeout);
        setSoTimeout(Constants.DEFAULT_CONNECTION_TIMEOUT);
              //父类AbstractProtocol的endpoint.setTcpNoDelay(tcpNoDelay);
        setTcpNoDelay(Constants.DEFAULT_TCP_NO_DELAY);
    }


    public NioEndpoint getEndpoint() {
        return ((NioEndpoint)endpoint);
    }

。。。省略部分代码
}

 1.3 AbstractEndpoint类(endpoint):

       

   AbstractEndpoint类 分析:

 org.apache.tomcat.util.net.AbstractEndpoint
 
  //1、init方法
 public final void init() throws Exception {
        testServerCipherSuitesOrderSupport();
        if (bindOnInit) {
            bind();
            bindState = BindState.BOUND_ON_INIT;
        }
    }

    protected void testServerCipherSuitesOrderSupport() {
        // Only test this feature if the user explicitly requested its use.
        if(!"".equals(getUseServerCipherSuitesOrder().trim()) && !JreCompat.isJre8Available()) {
            throw new UnsupportedOperationException(
                    sm.getString("endpoint.jsse.cannotHonorServerCipherOrder"));
        }
    }

       //2、start方法
     public final void start() throws Exception {
        if (bindState == BindState.UNBOUND) {
            bind();
            bindState = BindState.BOUND_ON_START;
        }
        startInternal();
    }


    //bind()具体实现方法在子类里 如:--》org.apache.tomcat.util.net.NioEndpoint
      /**
     * Initialize the endpoint.
     */
    @Override
    public void bind() throws Exception {

        serverSock = ServerSocketChannel.open();
        socketProperties.setProperties(serverSock.socket());
        InetSocketAddress addr = (getAddress()!=null?new InetSocketAddress(getAddress(),getPort()):new InetSocketAddress(getPort()));
        serverSock.socket().bind(addr,getBacklog());
        serverSock.configureBlocking(true); //mimic APR behavior
        serverSock.socket().setSoTimeout(getSocketProperties().getSoTimeout());

        // Initialize thread count defaults for acceptor, poller
        if (acceptorThreadCount == 0) {
            // FIXME: Doesn't seem to work that well with multiple accept threads
            acceptorThreadCount = 1;
        }
        if (pollerThreadCount <= 0) {
            //minimum one poller thread
            pollerThreadCount = 1;
        }
        stopLatch = new CountDownLatch(pollerThreadCount);

        // Initialize SSL if needed
        if (isSSLEnabled()) {
            SSLUtil sslUtil = handler.getSslImplementation().getSSLUtil(this);

            sslContext = sslUtil.createSSLContext();
            sslContext.init(wrap(sslUtil.getKeyManagers()),
                    sslUtil.getTrustManagers(), null);

            SSLSessionContext sessionContext =
                sslContext.getServerSessionContext();
            if (sessionContext != null) {
                sslUtil.configureSessionContext(sessionContext);
            }
            // Determine which cipher suites and protocols to enable
            enabledCiphers = sslUtil.getEnableableCiphers(sslContext);
            enabledProtocols = sslUtil.getEnableableProtocols(sslContext);
        }

        if (oomParachute>0) reclaimParachute(true);
        selectorPool.open();
    }


    //startInternal()具体实现方法在子类里--》org.apache.tomcat.util.net.NioEndpoint
      /**
     * Start the NIO endpoint, creating acceptor, poller threads.
     */
    @Override
    public void startInternal() throws Exception {

        if (!running) {
            running = true;
            paused = false;

            processorCache = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,
                    socketProperties.getProcessorCache());
            eventCache = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,
                            socketProperties.getEventCache());
            nioChannels = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,
                    socketProperties.getBufferPool());

            // Create worker collection   //创建Executor
            if ( getExecutor() == null ) {
                createExecutor();
            }

            initializeConnectionLatch();

            // Start poller threads      //启动poller线程
            pollers = new Poller[getPollerThreadCount()];
            for (int i=0; i<pollers.length; i++) {
                pollers[i] = new Poller();
                Thread pollerThread = new Thread(pollers[i], getName() + "-ClientPoller-"+i);
                pollerThread.setPriority(threadPriority);
                pollerThread.setDaemon(true);
                pollerThread.start();
            }

            startAcceptorThreads();  //启动Acceptor线程
        }
    }

  
   org.apache.tomcat.util.net.AbstractEndpoint类:

   protected final void startAcceptorThreads() {
        int count = getAcceptorThreadCount();
        acceptors = new Acceptor[count];

        f
tomcat如何处理请求 源码分析(一)( Connector 部分) tomcat源码分析共4个阶段 1.从整体把握tomcat的架构 2.分析springboot内嵌的tomcat是怎么启动的 3.分析tomcat如何接受处理请求1(HttpServlet之前) 4.分析tomcat如何接受处理请求2(HttpServlet处理) 目标: 本文主要分析一个来自浏览器的请求如何被tomcat接受,分析了从接收到socket至HttpServlet处理之间的各个方法调... 阅读详情

相关推荐

tomcat7源码分析

tomcat7源码分析本篇分为六个部分: tomcat基本框架 tomcat启动流程简介 tomcat启动流程源码分析 tomcat处理一个请求过程分析 .jsp生成.java和.class流程分析 apache beachmark性能测试 一、tomcat基本框架由三部分组成: 组件架构:组件搭起房梁 基于JMX:JMX管理组件等

没有伞的孩子只能快跑,如若不跑,只有一个结果:超过你的人回头鄙视你 5537

Tomcat源码分析--Connector(连接器)

Tomcat源码分析--Connector

每天进步一点点 2609

关于Nuttx操作系统

NuttX是一个成熟的实时操作系统

qq_28576837的博客 4092

ROS2 -Jazzy 一步一步(第一步_工作空间以及编译)

文件夹作用特点是否需要手动创建/维护src存放自己写的 ROS 2 包一个 workspace 可以有多个包需要手动创建build保存编译过程中的中间文件每个包生成对应子目录自动生成,可删除install保存编译完成后的功能包每个包有独立子目录,含可执行文件自动生成,可删除log保存编译日志用于查看错误原因、调试自动生成,不需修改colcon是 ROS 2 推荐使用的构建工具自动处理功能包的依赖和编译顺序一条命令就能编译整个工作空间。

jerrykorea的博客 1380

锁定放大器

锁定放大器

qq_46757048的博客 4706

基于 WebGoat 平台的 SQL 注入攻击

基于 WebGoat 平台的 SQL 注入攻击

Labulongdong的博客 3523

Oracle数据库RMAN异机恢复保姆级教程,按照步骤来操作,一次就成功!

本文深入解析RMAN异机恢复四大核心场景(演练、数据恢复、故障应急、测试环境搭建),提供从备份到验证的完整步骤和实战脚本。包含RAC到单机迁移技巧、路径调整、常见报错解决方案,助你彻底掌握数据库恢复的“救命技能”。DBA必备干货,强烈推荐

Jackyimi的博客 940

用python画带有正负值的条形图

常见的条形图只能表现数据的大小,却不能表现值的正负关系。但是其实仅仅使用`pandas`和`matplotlib`两个包就可以实现y轴负轴的添加以及负值的显示,并且可以为正负值分别设置不同的颜色,让条形图看上去更加清晰,易于我们分析

weixin_44853840的博客 5384

Python爬虫深度对抗:拼多多与得物价格监控系统中的设备指纹模拟与SkyWalking风控绕过实战

在电商数据采集领域,拼多多和得物(毒)APP堪称国产风控系统的“双子星”。前者依托于拼多多集团强大的安全团队,后者则以其独特的“潮流单品”数据价值和高昂的爬取成本闻名。对于任何一名爬虫工程师而言,能够稳定、高效地获取这两大平台的价格、库存、销量等动态信息,都是一项极具挑战性的技术试金石。传统的基于requests库的简单HTTP请求,在面对这两款应用时几乎寸步难行。你会发现,无论怎样伪造User-Agent、怎样轮换代理IP,返回的永远是状态码412403,或者是一个包含验证滑块设备异常的JSON错误。

2201_76125261的博客 865

PCL 切片法实现点云模型的体积测量、面积测量(二)

通过PCL VTK进行切片法,实现点云模型的体积测量、面积测量。

weixin_43896283的博客 1712

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

上一篇: Tomcat源码分析(三)-Container和Lifecycle
下一篇: Tomcat的源码分析(五)-Pipeline-value管道
Jacabe博客-编程
博客等级 码龄12年 320粉丝 57原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值