`
yayagepei
  • 浏览: 7141 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

tomcat源码分析系列之请求处理---关门打狗

 
阅读更多

    上回我们把请求放进来了,这回我们关上门,好好修理修理它,不折腾它一番,休想轻易出去。

要关门打狗,先得知道房子在哪才行啊,上回我们说到proces的处理被委托到Http11Processor类,它就是这套房子!Http11Processor的process方法,就是这间屋子:

/**
     * Process pipelined HTTP requests on the specified socket.
     *
     * @param socketWrapper Socket from which the HTTP requests will be read
     *               and the HTTP responses will be written.
     *  
     * @throws IOException error during an I/O operation
     */
    @Override
    public SocketState process(SocketWrapper<Socket> socketWrapper)
        throws IOException {
        RequestInfo rp = request.getRequestProcessor();
        //第一阶段
        rp.setStage(org.apache.coyote.Constants.STAGE_PARSE);

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

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

        if (maxKeepAliveRequests > 0) {
            socketWrapper.decrementKeepAlive();
        }
        
        int soTimeout = endpoint.getSoTimeout();

        int threadRatio = -1;   
        // These may return zero or negative values     
        // Only calculate a thread ratio when both are >0 to ensure we get a    
        // sensible result      
        if (endpoint.getCurrentThreadsBusy() >0 &&      
                endpoint.getMaxThreads() >0) {      
            threadRatio = (endpoint.getCurrentThreadsBusy() * 100)      
                    / endpoint.getMaxThreads();     
        }   
        // Disable keep-alive if we are running low on threads      
        if (threadRatio > getDisableKeepAlivePercentage()) {     
            socketWrapper.setKeepAliveLeft(0);
        }

        try {
            socket.getSocket().setSoTimeout(soTimeout);
        } catch (Throwable t) {
            ExceptionUtils.handleThrowable(t);
            log.debug(sm.getString("http11processor.socket.timeout"), t);
            error = true;
        }

        boolean keptAlive = socketWrapper.isKeptAlive();

        while (!error && keepAlive && !endpoint.isPaused()) {

            // Parsing the request header
            try {
                int standardTimeout = 0;
                if (keptAlive) {
                    if (keepAliveTimeout > 0) {
                        standardTimeout = keepAliveTimeout;
                    } else if (soTimeout > 0) {
                        standardTimeout = soTimeout;
                    }
                }
                /*
                 * When there is no data in the buffer and this is not the first
                 * request on this connection and timeouts are being used the
                 * first read for this request may need a different timeout to
                 * take account of time spent waiting for a processing thread.
                 * 
                 * This is a little hacky but better than exposing the socket
                 * and the timeout info to the InputBuffer
                 */
                if (inputBuffer.lastValid == 0 &&
                        socketWrapper.getLastAccess() > -1 &&
                        standardTimeout > 0) {

                    long queueTime = System.currentTimeMillis() -
                            socketWrapper.getLastAccess();
                    int firstReadTimeout;
                    if (queueTime >= standardTimeout) {
                        // Queued for longer than timeout but there might be
                        // data so use shortest possible timeout
                        firstReadTimeout = 1;
                    } else {
                        // Cast is safe since queueTime must be less than
                        // standardTimeout which is an int
                        firstReadTimeout = standardTimeout - (int) queueTime;
                    }
                    socket.getSocket().setSoTimeout(firstReadTimeout);
                    if (!inputBuffer.fill()) {
                        throw new EOFException(sm.getString("iib.eof.error"));
                    }
                }
                if (standardTimeout > 0) {
                    socket.getSocket().setSoTimeout(standardTimeout);
                }
                /**前面一堆道具,这里正式开始 这是用来读取Http头的,只是用来读取头的哦,
                  * 它负责将二进制流解析到对应的头部 后面我们会放出次方法的实现
                  **/
                inputBuffer.parseRequestLine(false);
                if (endpoint.isPaused()) {
                    // 503 - Service unavailable
                    response.setStatus(503);
                    adapter.log(request, response, 0);
                    error = true;
                } else {
                    request.setStartTime(System.currentTimeMillis());
                    keptAlive = true;
                    if (disableUploadTimeout) {
                        socket.getSocket().setSoTimeout(soTimeout);
                    } else {
                        socket.getSocket().setSoTimeout(connectionUploadTimeout);
                    }
                   /**走到这真坎坷,赶紧把到手的狗处理了吧--先处理头、脚
                    * 先把头部转化了
                    **/
                   inputBuffer.parseHeaders();
                }
            } catch (IOException e) {
                error = true;
                break;
            } catch (Throwable t) {
                ExceptionUtils.handleThrowable(t);
                if (log.isDebugEnabled()) {
                    log.debug(sm.getString("http11processor.header.parse"), t);
                }
                // 400 - Bad Request
                response.setStatus(400);
                adapter.log(request, response, 0);
                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) {
                    ExceptionUtils.handleThrowable(t);
                    if (log.isDebugEnabled()) {
                        log.debug(sm.getString("http11processor.request.prepare"), t);
                    }
                    // 400 - Internal Server Error
                    response.setStatus(400);
                    adapter.log(request, response, 0);
                    error = true;
                }
            }

            if (socketWrapper.getKeepAliveLeft() == 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 ||
                                (!isAsync() &&
                                statusDropsConnection(response.getStatus()));
                    }

                } catch (InterruptedIOException e) {
                    error = true;
                } catch (Throwable t) {
                    ExceptionUtils.handleThrowable(t);
                    log.error(sm.getString("http11processor.request.process"), t);
                    // 500 - Internal Server Error
                    response.setStatus(500);
                    adapter.log(request, response, 0);
                    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 && !isAsync())
                    inputBuffer.setSwallowInput(false);
                if (!isAsync())
                    //结束烹饪,结束请求
                    endRequest();
            } catch (Throwable t) {
                ExceptionUtils.handleThrowable(t);
                log.error(sm.getString("http11processor.request.finish"), t);
                // 500 - Internal Server Error
                response.setStatus(500);
                adapter.log(request, response, 0);
                error = true;
            }
            try {
                 //第五阶段
                 rp.setStage(org.apache.coyote.Constants.STAGE_ENDOUTPUT);
            } catch (Throwable t) {
                ExceptionUtils.handleThrowable(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
            if (!isAsync() || error) {
                inputBuffer.nextRequest();
                outputBuffer.nextRequest();
            }

            // If we don't have a pipe-lined request allow this thread to be
            // used by another connection
            if (isAsync() || error || inputBuffer.lastValid == 0) {
                break;
            }
            
            if (maxKeepAliveRequests > 0) {
                socketWrapper.decrementKeepAlive();
            }
        }
        //第七阶段:收工  上狗肉  
        rp.setStage(org.apache.coyote.Constants.STAGE_ENDED);
        if (error || endpoint.isPaused()) {
            return SocketState.CLOSED;
        } else if (isAsync()) {
            return SocketState.LONG;
        } else {
            if (!keepAlive) {
                return SocketState.CLOSED;
            } else {
                return SocketState.OPEN;
            }
        } 
    }

这个过程还是挺复杂的(你以为吃顿狗肉这么容易?),分七个阶段:

第一阶段:处理头部,可能抛503和400异常;

第二阶段:预处理请求体,主要解析请求的部分header和解析url,可能抛400异常;

第三阶段:处理请求(最核心的阶段),可能抛500异常;

第四阶段:结束请求,可能抛500异常;

第五阶段:更新统计计数,可能抛500异常;

第六阶段:整理线程,准备接受下一个请求;

第七阶段:结束

走完这七步,香喷喷的狗肉就上桌了!! ,不过先别动筷子,我们还要补上前面提到的几个方法:

处理头部:

/**
     * Read the request line. This function is meant to be used during the 
     * HTTP request header parsing. Do NOT attempt to read the request body 
     * using it.
     *
     * @throws IOException If an exception occurs during the underlying socket
     * read operations, or if the given buffer is not big enough to accommodate
     * the whole line.
     */
    @Override
    public boolean parseRequestLine(boolean useAvailableDataOnly)
    
        throws IOException {

        int start = 0;

        //
        // Skipping blank lines
        //

        byte chr = 0;
        do {

            // Read new bytes if needed
            if (pos >= lastValid) {
                if (!fill())
                    throw new EOFException(sm.getString("iib.eof.error"));
            }

            chr = buf[pos++];

        } while ((chr == Constants.CR) || (chr == Constants.LF));

        pos--;

        // Mark the current buffer position
        start = pos;

        //
        // Reading the method name
        // Method name is always US-ASCII
        //

        boolean space = false;

        while (!space) {

            // Read new bytes if needed
            if (pos >= lastValid) {
                if (!fill())
                    throw new EOFException(sm.getString("iib.eof.error"));
            }

            // Spec says no CR or LF in method name
            if (buf[pos] == Constants.CR || buf[pos] == Constants.LF) {
                throw new IllegalArgumentException(
                        sm.getString("iib.invalidmethod"));
            }
            // Spec says single SP but it also says be tolerant of HT
            if (buf[pos] == Constants.SP || buf[pos] == Constants.HT) {
                space = true;
                request.method().setBytes(buf, start, pos - start);
            }

            pos++;

        }

        
        // Spec says single SP but also says be tolerant of multiple and/or HT
        while (space) {
            // Read new bytes if needed
            if (pos >= lastValid) {
                if (!fill())
                    throw new EOFException(sm.getString("iib.eof.error"));
            }
            if (buf[pos] == Constants.SP || buf[pos] == Constants.HT) {
                pos++;
            } else {
                space = false;
            }
        }

        // Mark the current buffer position
        start = pos;
        int end = 0;
        int questionPos = -1;

        //
        // Reading the URI
        //

        boolean eol = false;

        while (!space) {

            // Read new bytes if needed
            if (pos >= lastValid) {
                if (!fill())
                    throw new EOFException(sm.getString("iib.eof.error"));
            }

            // Spec says single SP but it also says be tolerant of HT
            if (buf[pos] == Constants.SP || buf[pos] == Constants.HT) {
                space = true;
                end = pos;
            } else if ((buf[pos] == Constants.CR) 
                       || (buf[pos] == Constants.LF)) {
                // HTTP/0.9 style request
                eol = true;
                space = true;
                end = pos;
            } else if ((buf[pos] == Constants.QUESTION) 
                       && (questionPos == -1)) {
                questionPos = pos;
            }

            pos++;

        }

        request.unparsedURI().setBytes(buf, start, end - start);
        if (questionPos >= 0) {
            request.queryString().setBytes(buf, questionPos + 1, 
                                           end - questionPos - 1);
            request.requestURI().setBytes(buf, start, questionPos - start);
        } else {
            request.requestURI().setBytes(buf, start, end - start);
        }

        // Spec says single SP but also says be tolerant of multiple and/or HT
        while (space) {
            // Read new bytes if needed
            if (pos >= lastValid) {
                if (!fill())
                    throw new EOFException(sm.getString("iib.eof.error"));
            }
            if (buf[pos] == Constants.SP || buf[pos] == Constants.HT) {
                pos++;
            } else {
                space = false;
            }
        }

        // Mark the current buffer position
        start = pos;
        end = 0;

        //
        // Reading the protocol
        // Protocol is always US-ASCII
        //

        while (!eol) {

            // Read new bytes if needed
            if (pos >= lastValid) {
                if (!fill())
                    throw new EOFException(sm.getString("iib.eof.error"));
            }

            if (buf[pos] == Constants.CR) {
                end = pos;
            } else if (buf[pos] == Constants.LF) {
                if (end == 0)
                    end = pos;
                eol = true;
            }

            pos++;

        }

        if ((end - start) > 0) {
            request.protocol().setBytes(buf, start, end - start);
        } else {
            request.protocol().setString("");
        }
        
        return true;

    }

  这个方法没什么好说,接下来看

  /**
     * Parse an HTTP header.
     * 
     * @return false after reading a blank line (which indicates that the
     * HTTP header parsing is done
     */
    @SuppressWarnings("null") // headerValue cannot be null
    public boolean parseHeader()
        throws IOException {

        //
        // Check for blank line
        //

        byte chr = 0;
        while (true) {

            // Read new bytes if needed
            if (pos >= lastValid) {
                if (!fill())
                    throw new EOFException(sm.getString("iib.eof.error"));
            }

            chr = buf[pos];

            if ((chr == Constants.CR) || (chr == Constants.LF)) {
                if (chr == Constants.LF) {
                    pos++;
                    return false;
                }
            } else {
                break;
            }

            pos++;

        }

        // Mark the current buffer position
        int start = pos;

        //
        // Reading the header name
        // Header name is always US-ASCII
        //

        boolean colon = false;
        MessageBytes headerValue = null;

        while (!colon) {

            // Read new bytes if needed
            if (pos >= lastValid) {
                if (!fill())
                    throw new EOFException(sm.getString("iib.eof.error"));
            }

            if (buf[pos] == Constants.COLON) {
                colon = true;
                headerValue = headers.addValue(buf, start, pos - start);
            } else if (!HTTP_TOKEN_CHAR[buf[pos]]) {
                // If a non-token header is detected, skip the line and
                // ignore the header
                skipLine(start);
                return true;
            }

            chr = buf[pos];
            if ((chr >= Constants.A) && (chr <= Constants.Z)) {
                buf[pos] = (byte) (chr - Constants.LC_OFFSET);
            }

            pos++;

        }

        // Mark the current buffer position
        start = pos;
        int realPos = pos;

        //
        // Reading the header value (which can be spanned over multiple lines)
        //

        boolean eol = false;
        boolean validLine = true;

        while (validLine) {

            boolean space = true;

            // Skipping spaces
            while (space) {

                // Read new bytes if needed
                if (pos >= lastValid) {
                    if (!fill())
                        throw new EOFException(sm.getString("iib.eof.error"));
                }

                if ((buf[pos] == Constants.SP) || (buf[pos] == Constants.HT)) {
                    pos++;
                } else {
                    space = false;
                }

            }

            int lastSignificantChar = realPos;

            // Reading bytes until the end of the line
            while (!eol) {

                // Read new bytes if needed
                if (pos >= lastValid) {
                    if (!fill())
                        throw new EOFException(sm.getString("iib.eof.error"));
                }

                if (buf[pos] == Constants.CR) {
                    // Skip
                } else if (buf[pos] == Constants.LF) {
                    eol = true;
                } else if (buf[pos] == Constants.SP) {
                    buf[realPos] = buf[pos];
                    realPos++;
                } else {
                    buf[realPos] = buf[pos];
                    realPos++;
                    lastSignificantChar = realPos;
                }

                pos++;

            }

            realPos = lastSignificantChar;

            // Checking the first character of the new line. If the character
            // is a LWS, then it's a multiline header

            // Read new bytes if needed
            if (pos >= lastValid) {
                if (!fill())
                    throw new EOFException(sm.getString("iib.eof.error"));
            }

            chr = buf[pos];
            if ((chr != Constants.SP) && (chr != Constants.HT)) {
                validLine = false;
            } else {
                eol = false;
                // Copying one extra space in the buffer (since there must
                // be at least one space inserted between the lines)
                buf[realPos] = chr;
                realPos++;
            }

        }

        // Set the header value
        headerValue.setBytes(buf, start, realPos - start);

        return true;

    }
 
/**
     * After reading the request headers, we have to setup the request filters.
     */
    protected void prepareRequest() {

        http11 = true;
        http09 = false;
        contentDelimitation = false;
        expectation = false;
        
        prepareRequestInternal();

        if (endpoint.isSSLEnabled()) {
            request.scheme().setString("https");
        }
        MessageBytes protocolMB = request.protocol();
        if (protocolMB.equals(Constants.HTTP_11)) {
            http11 = true;
            protocolMB.setString(Constants.HTTP_11);
        } else if (protocolMB.equals(Constants.HTTP_10)) {
            http11 = false;
            keepAlive = false;
            protocolMB.setString(Constants.HTTP_10);
        } else if (protocolMB.equals("")) {
            // HTTP/0.9
            http09 = true;
            http11 = false;
            keepAlive = false;
        } else {
            // Unsupported protocol
            http11 = false;
            error = true;
            // Send 505; Unsupported HTTP version
            if (getLog().isDebugEnabled()) {
                getLog().debug(sm.getString("http11processor.request.prepare")+
                          " Unsupported HTTP version \""+protocolMB+"\"");
            }
            response.setStatus(505);
            adapter.log(request, response, 0);
        }

        MessageBytes methodMB = request.method();
        if (methodMB.equals(Constants.GET)) {
            methodMB.setString(Constants.GET);
        } else if (methodMB.equals(Constants.POST)) {
            methodMB.setString(Constants.POST);
        }

        MimeHeaders headers = request.getMimeHeaders();

        // Check connection header
        MessageBytes connectionValueMB = headers.getValue("connection");
        if (connectionValueMB != null) {
            ByteChunk connectionValueBC = connectionValueMB.getByteChunk();
            if (findBytes(connectionValueBC, Constants.CLOSE_BYTES) != -1) {
                keepAlive = false;
            } else if (findBytes(connectionValueBC,
                                 Constants.KEEPALIVE_BYTES) != -1) {
                keepAlive = true;
            }
        }

        MessageBytes expectMB = null;
        if (http11)
            expectMB = headers.getValue("expect");
        if ((expectMB != null)
            && (expectMB.indexOfIgnoreCase("100-continue", 0) != -1)) {
            getInputBuffer().setSwallowInput(false);
            expectation = true;
        }

        // Check user-agent header
        if ((restrictedUserAgents != null) && ((http11) || (keepAlive))) {
            MessageBytes userAgentValueMB = headers.getValue("user-agent");
            // Check in the restricted list, and adjust the http11
            // and keepAlive flags accordingly
            if(userAgentValueMB != null) {
                String userAgentValue = userAgentValueMB.toString();
                if (restrictedUserAgents != null &&
                        restrictedUserAgents.matcher(userAgentValue).matches()) {
                    http11 = false;
                    keepAlive = false;
                }
            }
        }

        // Check for a full URI (including protocol://host:port/)
        ByteChunk uriBC = request.requestURI().getByteChunk();
        if (uriBC.startsWithIgnoreCase("http", 0)) {

            int pos = uriBC.indexOf("://", 0, 3, 4);
            int uriBCStart = uriBC.getStart();
            int slashPos = -1;
            if (pos != -1) {
                byte[] uriB = uriBC.getBytes();
                slashPos = uriBC.indexOf('/', pos + 3);
                if (slashPos == -1) {
                    slashPos = uriBC.getLength();
                    // Set URI as "/"
                    request.requestURI().setBytes
                        (uriB, uriBCStart + pos + 1, 1);
                } else {
                    request.requestURI().setBytes
                        (uriB, uriBCStart + slashPos,
                         uriBC.getLength() - slashPos);
                }
                MessageBytes hostMB = headers.setValue("host");
                hostMB.setBytes(uriB, uriBCStart + pos + 3,
                                slashPos - pos - 3);
            }

        }

        // Input filter setup
        InputFilter[] inputFilters = getInputBuffer().getFilters();

        // Parse transfer-encoding header
        MessageBytes transferEncodingValueMB = null;
        if (http11)
            transferEncodingValueMB = headers.getValue("transfer-encoding");
        if (transferEncodingValueMB != null) {
            String transferEncodingValue = transferEncodingValueMB.toString();
            // Parse the comma separated list. "identity" codings are ignored
            int startPos = 0;
            int commaPos = transferEncodingValue.indexOf(',');
            String encodingName = null;
            while (commaPos != -1) {
                encodingName = transferEncodingValue.substring
                    (startPos, commaPos).toLowerCase(Locale.ENGLISH).trim();
                if (!addInputFilter(inputFilters, encodingName)) {
                    // Unsupported transfer encoding
                    error = true;
                    // 501 - Unimplemented
                    response.setStatus(501);
                    adapter.log(request, response, 0);
                }
                startPos = commaPos + 1;
                commaPos = transferEncodingValue.indexOf(',', startPos);
            }
            encodingName = transferEncodingValue.substring(startPos)
                .toLowerCase(Locale.ENGLISH).trim();
            if (!addInputFilter(inputFilters, encodingName)) {
                // Unsupported transfer encoding
                error = true;
                // 501 - Unimplemented
                if (getLog().isDebugEnabled()) {
                    getLog().debug(sm.getString("http11processor.request.prepare")+
                              " Unsupported transfer encoding \""+encodingName+"\"");
                }
                response.setStatus(501);
                adapter.log(request, response, 0);
            }
        }

        // Parse content-length header
        long contentLength = request.getContentLengthLong();
        if (contentLength >= 0 && !contentDelimitation) {
            getInputBuffer().addActiveFilter
                (inputFilters[Constants.IDENTITY_FILTER]);
            contentDelimitation = true;
        }

        MessageBytes valueMB = headers.getValue("host");

        // Check host header
        if (http11 && (valueMB == null)) {
            error = true;
            // 400 - Bad request
            if (getLog().isDebugEnabled()) {
                getLog().debug(sm.getString("http11processor.request.prepare")+
                          " host header missing");
            }
            response.setStatus(400);
            adapter.log(request, response, 0);
        }

        parseHost(valueMB);

        if (!contentDelimitation) {
            // If there's no content length 
            // (broken HTTP/1.0 or HTTP/1.1), assume
            // the client is not broken and didn't send a body
            getInputBuffer().addActiveFilter
                    (inputFilters[Constants.VOID_FILTER]);
            contentDelimitation = true;
        }

        // Advertise sendfile support through a request attribute
        if (endpoint.getUseSendfile()) {
            request.setAttribute("org.apache.tomcat.sendfile.support",
                    Boolean.TRUE);
        }
        
        // Advertise comet support through a request attribute
        if (endpoint.getUseComet()) {
            request.setAttribute("org.apache.tomcat.comet.support",
                    Boolean.TRUE);
        }
        // Advertise comet timeout support
        if (endpoint.getUseCometTimeout()) {
            request.setAttribute("org.apache.tomcat.comet.timeout.support",
                    Boolean.TRUE);
        }
    }
 

这几个方法没什么好说,感兴趣就看一下就行了。

分享到:
评论

相关推荐

    apache-tomcat-9.0.45-windows-x64

    apache-tomcat-9.0.45-windows-x64apache-tomcat-9.0.45-windows-x64apache-tomcat-9.0.45-windows-x64apache-tomcat-9.0.45-windows-x64apache-tomcat-9.0.45-windows-x64apache-tomcat-9.0.45-windows-x64apache-...

    tomcat 源码分析系列文档

    4. "Tomcat源码分析(4)容器处理链接之责任链模式.doc":分析了Tomcat如何利用责任链模式来处理请求,使得请求可以被多个处理器(如过滤器)有序处理。 5. "tomcat加载类的顺序.doc":详细说明了Tomcat加载类的具体...

    tomcat9+tomcat-cluster-redis-session-manager_4.0.zip

    "tomcat9+tomcat-cluster-redis-session-manager_4.0.zip"这个文件组合涉及到的是在Tomcat 9上实现负载均衡以及使用Redis作为Session管理器的高级配置。 首先,Tomcat 9是Apache Tomcat服务器的一个版本,它是Java ...

    tomcat-connectors-1.2.40-windows-x86_64-httpd-2.4.x

    标题中的"tomcat-connectors-1.2.40-windows-x86_64-httpd-2.4.x"指的是Tomcat服务器与Apache HTTPD服务器之间的连接器版本1.2.40,专为64位Windows系统设计,并且兼容HTTPD服务器的2.4.x版本。这个连接器,也被称为...

    Tomcat源码apache-tomcat-8.5.47-src.zip

    `apache-tomcat-8.5.47-src.zip`这个压缩包包含了Tomcat 8.5.47版本的完整源代码,这对于想要研究Tomcat工作原理、优化性能或者进行自定义扩展的开发者非常有价值。 在开始学习Tomcat源码之前,首先需要了解一些...

    tomcat-redis-session-manager-1.2-tomcat-6&7

    标题 "tomcat-redis-session-manager-1.2-tomcat-6&7" 指的是一个用于在Tomcat服务器中集成Redis作为session管理器的组件。这个组件使得Web应用程序可以利用Redis分布式缓存系统来存储和管理用户的会话数据,从而...

    tomcat-redis-session-manager-2.0.0.jar

    tomcat-redis-session-manager-2.0.0.jar,可用于Tomcat8下Redis的Session共享,亲测可用,还需要下载另外两个jar包:commons-pool2-2.4.2.jar和jedis-2.9.0.jar,maven仓库有,此处不再上传

    tomcat-redis-session-manager源码

    《深入解析Tomcat-Redis-Session-Manager源码》 在现代Web应用中,服务器端会话管理是一个至关重要的部分,特别是在高并发、分布式环境中。Tomcat作为最流行的Java Servlet容器,提供了丰富的功能来支持这一需求。...

    Tomcat7下载(apache-tomcat-7.0.85)

    Tomcat7下载(apache-tomcat-7.0.85)Tomcat7下载(apache-tomcat-7.0.85)Tomcat7下载(apache-tomcat-7.0.85)Tomcat7下载(apache-tomcat-7.0.85)

    最新版windows apache-tomcat-8.5.59-windows-x64.zip

    这个最新的版本“apache-tomcat-8.5.59-windows-x64.zip”是专门为Windows 64位操作系统设计的。让我们深入探讨一下这个版本包含的知识点。 首先,Apache Tomcat 8.5.x系列是Tomcat服务器的一个稳定版本,它在功能...

    最新版windows apache-tomcat-8.5.69-windows-x64.zip

    这个最新的版本,"apache-tomcat-8.5.69-windows-x64.zip",是专为Windows 64位操作系统设计的。在这个版本中,用户可以享受到更加稳定和高效的服务。 Tomcat 8.5系列是Apache Tomcat的一个重要版本,它引入了许多...

    apache-tomcat-9.0.41-windows-x64压缩包版.zip

    这个"apache-tomcat-9.0.41-windows-x64压缩包版.zip"文件是专为64位Windows系统设计的Apache Tomcat 9.0.41版本的压缩包,包含了运行和管理Java web应用程序所需的所有组件。 Apache Tomcat作为应用服务器,它的...

    从零手写Tomcat【源码】【abl-tomcat-004】【基础完善-处理动态资源请求】

    从零手写Tomcat【源码】【abl-tomcat-004】【基础完善-处理动态资源请求】 文章地址:https://blog.csdn.net/m0_37969197/article/details/124096403 目录地址:...

    tomcat共享session tomcat-redis-session-manager-2.0.0.jar包下载

    tomcat-redis-session-manager-2.0.0.jar包,不用自己打包了,tomcat共享session到redis中,解决分布式应用的状态问题。

    官方原版apache-tomcat-9.0.33-windows-x64.zip

    这个"官方原版apache-tomcat-9.0.33-windows-x64.zip"是Apache Tomcat的第9.0.33版本,专为64位Windows操作系统设计。以下是关于这个版本的Apache Tomcat的详细知识: 1. **版本号**:9.0.33代表了Tomcat的版本。每...

    最新版windows apache-tomcat-8.5.66-windows-x64.zip

    "apache-tomcat-8.5.66-windows-x64.zip"是专门为64位Windows系统设计的最新版本,确保在处理大量并发请求时具备更好的性能和稳定性。 在8.5.x系列中,Tomcat 8.5.66包含了多项改进和修复,以提升安全性和性能。这...

    官方原版apache-tomcat-8.5.51-windows-x64.zip 64位

    该压缩包"apache-tomcat-8.5.51-windows-x64.zip"包含以下关键组件: 1. **bin目录**:这个目录包含了用于启动、停止和管理Tomcat的各种脚本,如`catalina.bat`(Windows批处理文件)和`startup.sh`(Unix/Linux ...

    apache-tomcat-11.0.0-M17-windows-x64.zip

    2. **解压与配置**:将`apache-tomcat-11.0.0-M17`文件夹解压到所需的目录,然后配置环境变量`CATALINA_HOME`指向该目录。 3. **启动与停止**:在Tomcat的`bin`目录下,可以通过执行`startup.bat`启动Tomcat,执行`...

    Apache-tomcat-7.0.109-Windows-x64

    在本文中,我们将深入探讨与"Apache-tomcat-7.0.109-Windows-x64"相关的知识,包括Tomcat的概述、版本7.0.109的特点、在Windows 64位系统上的安装与配置,以及Java环境的设置等关键内容。 首先,让我们了解Tomcat的...

    tomcat-native-1.2.25-src-build

    【标题】"tomcat-native-1.2.25-src-build" 涉及到的知识点主要集中在Apache Tomcat服务器、 APR (Apache Portable Runtime) 库以及如何编译和使用这些组件。 Apache Tomcat是一款开源的Java Servlet容器,广泛应用...

Global site tag (gtag.js) - Google Analytics