`
nod0620
  • 浏览: 19999 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

tomcat的Http11Protocol,Http11Processor

阅读更多

  上文说到在JIoEndpoint类中处理请求最终是调用到内部接口Handler的process()方法,而Handler的实现类是Http11Protocol的内部类Http11ConnectionHandler,Http11ConnectionHandler又委托Http11Processor进行处理,tomcat在这个地方我觉得类的设计不是很优雅啊,内部类用的太多,导致阅读的时候有困难(好处是内部类持有外部类的的引用,不用使用显示的new)。

先把Http11ConnectionHandler的类附上:

    protected static class Http11ConnectionHandler implements Handler {

        protected Http11Protocol proto;
        protected AtomicLong registerCount = new AtomicLong(0);
        protected RequestGroupInfo global = new RequestGroupInfo();

        //无界线程安全队列,不会阻塞,自己加工的原因是processorCache的数量是有限的,为了线程和性能考虑
        protected ConcurrentLinkedQueue<Http11Processor> recycledProcessors = 
            new ConcurrentLinkedQueue<Http11Processor>() {
            protected AtomicInteger size = new AtomicInteger(0);
            public boolean offer(Http11Processor processor) {
                boolean offer = (proto.processorCache == -1) ? true : (size.get() < proto.processorCache);
                //avoid over growing our cache or add after we have stopped
                boolean result = false;
                if ( offer ) {
                    result = super.offer(processor);
                    if ( result ) {
                        size.incrementAndGet();
                    }
                }
                if (!result) unregister(processor);
                return result;
            }
            
            public Http11Processor poll() {
                Http11Processor result = super.poll();
                if ( result != null ) {
                    size.decrementAndGet();
                }
                return result;
            }
            
            public void clear() {
                Http11Processor next = poll();
                while ( next != null ) {
                    unregister(next);
                    next = poll();
                }
                super.clear();
                size.set(0);
            }
        };

        Http11ConnectionHandler(Http11Protocol proto) {
            this.proto = proto;
        }

        public boolean process(Socket socket) {
        	//processorCache里面没有processor
            Http11Processor processor = recycledProcessors.poll();
            try {

                if (processor == null) {
                    processor = createProcessor();
                }

                if (processor instanceof ActionHook) {
                    ((ActionHook) processor).action(ActionCode.ACTION_START, null);
                }

                if (proto.isSSLEnabled() && (proto.sslImplementation != null)) {
                    processor.setSSLSupport
                        (proto.sslImplementation.getSSLSupport(socket));
                } else {
                    processor.setSSLSupport(null);
                }
                
                processor.process(socket);
                return false;

            } catch(java.net.SocketException e) {
                // SocketExceptions are normal
                Http11Protocol.log.debug
                    (sm.getString
                     ("http11protocol.proto.socketexception.debug"), e);
            } catch (java.io.IOException e) {
                // IOExceptions are normal
                Http11Protocol.log.debug
                    (sm.getString
                     ("http11protocol.proto.ioexception.debug"), e);
            }
            // Future developers: if you discover any other
            // rare-but-nonfatal exceptions, catch them here, and log as
            // above.
            catch (Throwable e) {
                // any other exception or error is odd. Here we log it
                // with "ERROR" level, so it will show up even on
                // less-than-verbose logs.
                Http11Protocol.log.error
                    (sm.getString("http11protocol.proto.error"), e);
            } finally {
                //       if(proto.adapter != null) proto.adapter.recycle();
                //                processor.recycle();

                if (processor instanceof ActionHook) {
                    ((ActionHook) processor).action(ActionCode.ACTION_STOP, null);
                }
                recycledProcessors.offer(processor);
            }
            return false;
        }
}

  看process()方法前,我们看另外一个有意思的东西,ConcurrentLinkedQueue的子类,自己实现的原因是ConcurrentLinkedQueue是无界的,非阻塞的,而这个需要的是有界的,查了下jdk的concurrent包,没有现有的有界的,非阻塞的实现类,所有这里自己实现个,还有实现这个主要还和jmx的注册联系在一起,这里由于processorCache==-1,长度限制这个原因不存在。主要看process()这个方法

    首先,从ConcurrentLinkedQueue中取得一个Http11Processor,没有的话就创建一个。

    由于Http11Processor是ActionHook的实现,ActionHook的action方法被执行,这个以后再说,这里是   ActionCode.ACTION_START的事件的执行

    接着是SSL的一些的设置,在https的时候有用

    接着重点,调用Http11Processor的process的方法

    后面就是收拾残局了,什么错误啊,Http11Processor的回收了,ACTION_STOP的事件执行了

 

    转到Http11Processor的process()的方法:

 

   public void process(Socket theSocket)
        throws IOException {
        RequestInfo rp = request.getRequestProcessor();
        rp.setStage(org.apache.coyote.Constants.STAGE_PARSE);

        // Set the remote address
        remoteAddr = null;
        remoteHost = null;
        localAddr = null;
        localName = null;
        remotePort = -1;
        localPort = -1;

        // Setting up the I/O
        this.socket = theSocket;
        inputBuffer.setInputStream(socket.getInputStream());
        outputBuffer.setOutputStream(socket.getOutputStream());

        // Error flag
        error = false;
        keepAlive = true;

        int keepAliveLeft = maxKeepAliveRequests;
        int soTimeout = endpoint.getSoTimeout();

        // When using an executor, these values may return non-positive values
        int curThreads = endpoint.getCurrentThreadsBusy();
        int maxThreads = endpoint.getMaxThreads();
        if (curThreads > 0 && maxThreads > 0) {
            // Only auto-disable keep-alive if the current thread usage % can be
            // calculated correctly
            if ((curThreads*100)/maxThreads > 75) {
                keepAliveLeft = 1;
            }
        }
        
        try {
            socket.setSoTimeout(soTimeout);
        } catch (Throwable t) {
            log.debug(sm.getString("http11processor.socket.timeout"), t);
            error = true;
        }

        boolean keptAlive = false;

        //keepAlive:当处理完用户发起的 HTTP 请求后是否立即关闭 TCP 连接
        while (started && !error && keepAlive) {

            // Parsing the request header
            try {
                if (keptAlive) {
                    if (keepAliveTimeout > 0) {
                        socket.setSoTimeout(keepAliveTimeout);
                    }
                    else if (soTimeout > 0) {
                        socket.setSoTimeout(soTimeout);
                    }
                }
                
                //解析http header 的第一行数据
                inputBuffer.parseRequestLine();
                request.setStartTime(System.currentTimeMillis());
                keptAlive = true;
                if (disableUploadTimeout) {
                    socket.setSoTimeout(soTimeout);
                } else {
                    socket.setSoTimeout(timeout);
                }
              //解析http header 的除第一行数据以外的数据
                inputBuffer.parseHeaders();
            } catch (IOException e) {
                error = true;
                break;
            } catch (Throwable t) {
                if (log.isDebugEnabled()) {
                    log.debug(sm.getString("http11processor.header.parse"), t);
                }
                // 400 - Bad Request
                response.setStatus(400);
                error = true;
            }

            if (!error) {
                // Setting up filters, and parse some request headers
                rp.setStage(org.apache.coyote.Constants.STAGE_PREPARE);
                try {
                    prepareRequest();
                } catch (Throwable t) {
                    if (log.isDebugEnabled()) {
                        log.debug(sm.getString("http11processor.request.prepare"), t);
                    }
                    // 400 - Internal Server Error
                    response.setStatus(400);
                    error = true;
                }
            }

            if (maxKeepAliveRequests > 0 && --keepAliveLeft == 0)
                keepAlive = false;

            // Process the request in the adapter
            if (!error) {
                try {
                    rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE);
                    adapter.service(request, response);
                    // Handle when the response was committed before a serious
                    // error occurred.  Throwing a ServletException should both
                    // set the status to 500 and set the errorException.
                    // If we fail here, then the response is likely already
                    // committed, so we can't try and set headers.
                    if(keepAlive && !error) { // Avoid checking twice.
                        error = response.getErrorException() != null ||
                                statusDropsConnection(response.getStatus());
                    }

                } catch (InterruptedIOException e) {
                    error = true;
                } catch (Throwable t) {
                    log.error(sm.getString("http11processor.request.process"), t);
                    // 500 - Internal Server Error
                    response.setStatus(500);
                    error = true;
                }
            }

            // Finish the handling of the request
            try {
                rp.setStage(org.apache.coyote.Constants.STAGE_ENDINPUT);
                // If we know we are closing the connection, don't drain input.
                // This way uploading a 100GB file doesn't tie up the thread 
                // if the servlet has rejected it.
                if(error)
                    inputBuffer.setSwallowInput(false);
                inputBuffer.endRequest();
            } catch (IOException e) {
                error = true;
            } catch (Throwable t) {
                log.error(sm.getString("http11processor.request.finish"), t);
                // 500 - Internal Server Error
                response.setStatus(500);
                error = true;
            }
            try {
                rp.setStage(org.apache.coyote.Constants.STAGE_ENDOUTPUT);
                outputBuffer.endRequest();
            } catch (IOException e) {
                error = true;
            } catch (Throwable t) {
                log.error(sm.getString("http11processor.response.finish"), t);
                error = true;
            }

            // If there was an error, make sure the request is counted as
            // and error, and update the statistics counter
            if (error) {
                response.setStatus(500);
            }
            request.updateCounters();

            rp.setStage(org.apache.coyote.Constants.STAGE_KEEPALIVE);

            // Don't reset the param - we'll see it as ended. Next request
            // will reset it
            // thrA.setParam(null);
            // Next request
            inputBuffer.nextRequest();
            outputBuffer.nextRequest();

        }

        rp.setStage(org.apache.coyote.Constants.STAGE_ENDED);

        // Recycle
        inputBuffer.recycle();
        outputBuffer.recycle();
        this.socket = null;
        // Recycle ssl info
        sslSupport = null;
    }

    Http11Processor很夸张的又引用到JIoEndpoint(感觉很乱,是否有循环有用的感觉?)

    这里有一个很有意思的变量keepAliveLeft,一开始的时候等于maxKeepAliveRequests,也就是创建Processor的时候传入的Http11Protocol的maxKeepAliveRequests,默认为100,当JIoEndpoint的curThreads(当前已经用的线程数)/maxThreads>0.75,maxKeepAliveRequests马上可怜的变成1,这里可以认为是过载保护。

   当开始标志,没有错误标志等设置后,就开始真正解析这个传入的socket了.

   在这之前实例化InternalInputBuffer,InternalOutputBuffer两个类,对应socket的输入和输出,是可以重复使用的高性能的类。

   首先调用InternalInputBuffer的parseRequestLine()方法解析http的请求行,即'请求方法 请求uri http版本'那一行

  接着解析请求头InternalInputBuffer的parseHeaders()方法干这个活

 

    public void parseHeaders()
        throws IOException {

        while (parseHeader()) {
        }

        parsingHeader = false;
        end = pos;

    }

   while循环解析每一行,请求头有个专门的类MimeHeaders存放,里面一般存放的是MessageBytes,简单的说一行就是MessageBytes,这个类有char,byte,String等表示,好处是很方便,任何三类都可以处理,对于涉及编码的来说

,char和byte可以非常方便的进行原生态的表示

   接着是调用prepareRequest()方法,其实在parseHeaders()中进行了http headers的解析,这里只是拿出来用而已,只是把org.apache.coyote.Request的属性设置好而已

 

    Http11Processor的process方法就到这里

    回到Http11ConnectionHandler的process()方法,最后面有代码:

        finally {
                //       if(proto.adapter != null) proto.adapter.recycle();
                //                processor.recycle();

                if (processor instanceof ActionHook) {
                    ((ActionHook) processor).action(ActionCode.ACTION_STOP, null);
                }
                recycledProcessors.offer(processor);
            }

    这里调用了Http11Processor的action的方法,我们看下这个方法:

 

    	
        if (actionCode == ActionCode.ACTION_COMMIT) {
        	
            // Commit current response  只有在response中触发这个分支
        	
        	
        	//一般来说现在response.isCommitted()是false的,但tomcat中很多地方都有这样的判断,主要是为了稳定性考虑(不存在多线程问题?)
            if (response.isCommitted())
                return;

            // Validate and write response headers
            prepareResponse();
            try {
                outputBuffer.commit();
            } catch (IOException e) {
                // Set error flag
                error = true;
            }

        } 
 

    第一个分支就是,注意  prepareResponse(); 这里是准备org.apache.coyote.Response方法了,这个和request的处理差不多,是设置http协议响应头的一些内容

 

  准备好的org.apache.coyote.Request和org.apache.coyote.Response在CoyoteAdapter里面会被用到,返回给tomcat的Connector组件,这个下回再说。

 

 

  哇,真长!

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

分享到:
评论

相关推荐

    tomcat-connectors-1.2.40-src.zip

    1. 请求接收:`Http11Processor`类是Http11Protocol的核心,它负责解析接收到的HTTP请求数据,将其转化为内部可以处理的`Request`对象。 2. 链接管理:`AprSocketAcceptor`(若使用了Apache Portable Runtime,即...

    http协议详解和tomcat源码,how tomcat works

    例如,可以通过分析`org.apache.coyote.http11.Http11Processor`类来理解HTTP请求的处理过程;研究`org.apache.catalina.connector.Connector`和`org.apache.tomcat.util.net.SocketWrapper`来了解网络通信细节;...

    Tomcat---Connector 分析.docx

    `Connector`的实现基于`ProtocolHandler`,不同的`ProtocolHandler`对应不同的连接方式,例如`Http11Protocol`使用传统的Socket连接,而`Http11NioProtocol`则利用Java的非阻塞I/O(NIO)进行连接。`ProtocolHandler...

    tomcat-connectors-1.2.46-src

    这个包是Apache Tomcat服务器的核心组件之一,主要负责处理HTTP和AJP(Apache JServ Protocol)协议,使得Web应用程序能够与Web服务器进行通信。Tomcat作为开源的Java Servlet容器,它不仅支持Servlet规范,还实现了...

    tomcat笔记_已.docx

    Tomcat 是一个广泛使用的开源 Java Web 应用服务器,它遵循 Servlet 和 JSP 规范,用于部署和运行动态 Web 应用。本笔记主要探讨 Tomcat 8.5 的整体架构、各个组件及其相互关系。 1. Tomcat 总体架构 Tomcat 的核心...

    基于全局储存的新思路 _ Tomcat的一种通用回显方法研究1

    在分析Tomcat源码的过程中,作者发现Http11Processor类(继承自AbstractProcessor)持有Request和Response的引用,这两个引用是final类型的,一旦赋值就不会改变。因此,只要能获取到Http11Processor实例,就能得到...

    从连接器组件看Tomcat的线程模型——BIO模式(推荐)

    Tomcat中的BIO模式主要由`Http11Protocol`组件实现,它负责处理HTTP 1.1协议的通信。`Http11Protocol`包含了从客户端套接字接收请求,处理请求,然后向客户端发送响应的全过程。在Tomcat中,BIO模式下,`JIoEndpoint...

    Tomcat 网络通信模型剖析 (1)1

    例如,将`protocol`设置为`"HTTP/1.1"`、`"Http11NioProtocol"`、`"Http11Nio2Protocol"`或`"Http11AprProtocol"`,分别对应BIO、NIO、NIO2和APR。 在源码层面,BIO模型中,`Acceptor`线程负责接受新连接,而`...

    S03-tomcat源码调试以及架构学习1

    2. **Protocol Handler**: 解析接收到的HTTP请求,如HTTP/1.1或HTTP/2。 3. **Pipeline(管道)**: 请求被传递到一个包含多个Valves(阀门)的管道,每个Valve执行特定的任务,如认证、URL重写、会话管理等。 4. **...

    COS——R.log

    at org.apache.coyote.http11.Http11BaseProtocol$Http11ConnectionHandler.processConnection(Http11BaseProtocol.java:665) at org.apache.tomcat.util.net.PoolTcpEndpoint.processSocket(PoolTcpEndpoint.java...

    tomcat-exp:Tomcat原始码

    重点关注`org.apache.coyote`包下的`AbstractProtocol`、`Http11Protocol`等类。 4. **Jasper组件** Jasper是Tomcat的JSP引擎,用于编译和执行JSP页面。通过阅读源码,可以了解JSP是如何被转换成Servlet的,以及...

    commons-beanutils-1.7.0

    at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:581) at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:447) at java.lang.Thread....

    spring-boot-https-源码.rar

    Tomcat的`AbstractHttpClientUpgradeHandler`和`AbstractHttp11Processor`类负责处理HTTP升级到HTTPS的请求,以及处理HTTPS的HTTP请求。在Spring Boot中,`TomcatServletWebServerFactory`会根据SSLContext配置...

    解决struts2下载异常的jar包 struts2-sunspoter-stream-1.0.jar

    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:298) at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:852) at org.apache.coyote.http11.Http11Protocol$...

    struts2漏洞S2-0211

    - `parseRequestLine()`:解析请求行(method、uri及protocol)。 - `parseHeaders()`:解析HTTP头部信息(host、ua等)。 - **准备请求**:通过`prepareRequest()`方法组装request filter,用于处理HTTP消息体。 - **...

    Spring boot 配置参数一览.pdf

    - `server.tomcat.protocol-header`:SSL转发头的名称,默认为x-forwarded-proto。 - `server.tomcat.remote-ip-header`:远程IP头的名称,默认为x-forwarded-for。 - `server.tomcat.basedir`:Tomcat基准目录,...

    看透springMvc源代码分析与实践

    7.5.5 处理HTTP协议的Processor80 7.5.6 适配器Adapter81 第二篇 俯视Spring MVC 第8章 Spring MVC之初体验84 8.1 环境搭建84 8.2 Spring MVC最简单的配置84 8.2.1 在web.xml中配置Servlet85 8.2.2 创建...

    jmeter学习概要

    它最初被设计出来是为了评估 Tomcat 的前身 JServ 的执行效率,随着项目的成熟和发展,JMeter 的应用场景已经远远超出了最初的范围,现在它不仅用于 Web 应用的压力测试,还扩展到了其他多种类型的测试场景,包括但...

Global site tag (gtag.js) - Google Analytics