톰캣 컨테이너 처리 요구는 얼마나

머리말

이전 " 커넥터 Tomcat이 설계하는 방법이다 "우리는 컨테이너 처리로 이동, 커넥터 포트가 연결 요청에 대한 액세스 네트워크를 모니터링 할 책임이 알고, 다음 요청 서블릿 표준에 맞춰 변환, 톰캣 커넥터 설계를 설명 다음, 우리는 컨테이너에 요청, 컨테이너가 어떻게 요청 있는지 확인하기 위해 문서에이 문서의 아이디어를 따릅니다.

설명 :이 버전은 9.0.21 Tomcat이 제로 독자를하지 않는 것이 좋습니다입니다.

어댑터에서 말하기

우리는 문서에 따라 계속 Adapter소스를 다음과 같이 문서의 끝 부분에 소스 코드를 분석하기 위해 계속 :

  //源码1.类:  CoyoteAdapter implements Adapter
public void service(org.apache.coyote.Request req, org.apache.coyote.Response res)
            throws Exception {

        Request request = (Request) req.getNote(ADAPTER_NOTES);
        Response response = (Response) res.getNote(ADAPTER_NOTES);
        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);
            }
            
    }

复制代码

위의 주요 효과는 용기의 근원에 도착하고 호출하는 것입니다 getPipeline()획득을 Pipeline, 그리고 마지막에 invoke호출의이 살펴 보자 Pipeline수행되는 정도.

//源码2.Pipeline接口
public interface Pipeline extends Contained {
  public Valve getBasic();
  public void setBasic(Valve valve);
  public void addValve(Valve valve);
  public Valve[] getValves();
  public void removeValve(Valve valve);
  public Valve getFirst();
  public boolean isAsyncSupported();
  public void findNonAsyncValves(Set<String> result);
}
//源码3. Valve接口
public interface Valve {
 public Valve getNext();
 public void setNext(Valve valve);
 public void backgroundProcess();
 public void invoke(Request request, Response response)
        throws IOException, ServletException;
 public boolean isAsyncSupported();
复制代码

우리는 문자 그대로 해석 할 수있는 Pipeline파이프 라인 있다는 Valve사실 밸브이다가, 톰캣의 역할은 거의 그대로이다. 각 컨테이너는 덕트를 갖는 밸브 및 복수 개의 파이프가있다. 우리는 뒤에 분석에 의해 보여 주었다.

파이프 - 밸브 (파이프 라인 밸브)

우리는 위의 소스를 참조 Pipeline하고 Valve, 인터페이스를 Pipeline주로 설정 Valve하고, Valve연결리스트는 다음 될 수있다 invoke방법이라고합니다. 우리는이 소스에서 돌아 보면 :

//源码4
connector.getService().getContainer().getPipeline().getFirst().invoke(
                        request, response);
复制代码

여기에 파이프 라인 컨테이너에 직접 액세스되어 제 1 얻을 Valve전화를. 우리가 전에 언급 한 바와 같이 Valve연결리스트, 여기에 첫 번째 호출입니다, 즉, 당신은 다음에 의해 마지막 호출에 갈 수 있습니다. 우리는 우리의 첫 번째 기사 "다시 볼 SpringBoot에서 Tomcat이 어떻게 시작하는 것입니다 앞서 언급 한"컨테이너는 각각 4 개의 하위 용기에 나누어 Engine,, Host, Context, Wrapper그들은 또한 부모와 자식 관계이다, Engine> Host> Context> Wrapper.

나는 이전에 각 컨테이너 a를 언급 한 Pipeline다음이가 각성 어떻게? 우리는 컨테이너 인터페이스의 소스 코드를 찾을 수 있습니다 볼 수 Pipeline는 컨테이너 인터페이스 정의의 기본 속성입니다 :

//源码5.
public interface Container extends Lifecycle {
    //省略其他代码
  /**
     * Return the Pipeline object that manages the Valves associated with
     * this Container.
     *
     * @return The Pipeline
     */
    public Pipeline getPipeline();
    
}
复制代码

우리는 각 컨테이너는 파이프를 (가 알고 Pipeline많은 파이프 라인 밸브 (있다,) Valve,) Valve호출 체인 될 수있다, 그 다음 질문은, 부모 컨테이너 파이프입니다 Valve방법 하위 컨테이너 호출 Valve이? 에서 Pipeline구현 클래스 StandardPipeline, 우리는 다음과 같은 소스를 발견 :

 /**
// 源码6.
     * The basic Valve (if any) associated with this Pipeline.
     */
    protected Valve basic = null;
       /**
     * The first valve associated with this Pipeline.
     */
    protected Valve first = null;
    
     public void addValve(Valve valve) {

        //省略部分代码

        // 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) {
                //这里循环设置Valve,保证最后一个是basic
                if (current.getNext() == basic) {
                    current.setNext(valve);
                    valve.setNext(basic);
                    break;
                }
                current = current.getNext();
            }
        }

        container.fireContainerEvent(Container.ADD_VALVE_EVENT, valve);
    }
复制代码

위의 코드는 우리가 알고있는 basicA (파이프 인 Pipeline만큼 제 1 밸브는 모든 호출 체인을 완성 할 수있는 용기의 밸브와 같이 지속 이유에 스탠드의 마지막 밸브). 우리는 요청을 사용하여, 우리는 디버그 아래로 우리의 추측 있는지 CoyoteAdapter입니다 service다음과 같이 브레이크 포인트 방법을 만들기 위해, 효과는 다음과 같습니다

어댑터가 호출하는 용기, 호출 할 때 여기에서 우리가 알 수있는 Engine파이프 라인의 기본 가치이며, 하나 개의 밸브를, StandardEngineValve. 우리는 다음과 같이 밸브 호출 방법이 발견 :

//源码7.
public final void invoke(Request request, Response response)
        throws IOException, ServletException {

        // Select the Host to be used for this Request
        Host host = request.getHost();
        if (host == null) {
            // HTTP 0.9 or HTTP 1.0 request without a host when no default host
            // is defined. This is handled by the CoyoteAdapter.
            return;
        }
        if (request.isAsyncSupported()) {
            request.setAsyncSupported(host.getPipeline().isAsyncSupported());
        }

        // Ask this Host to process this request
        host.getPipeline().getFirst().invoke(request, response);
    }
复制代码

우리는 다음과 같은 결과를 볼 수 디버깅을 계속 :

그래서 여기에 basic실제로 호출 Host컨테이너 파이프 ( Pipeline) 및 밸브 ( Valve), 즉, 파이프 라인의 각 컨테이너는 basic서브 밸브 컨테이너를 호출 할 책임이있다. 나는 나타내는 다이어그램을 사용 :

용기의 내부 커넥터에서 톰캣 전송 요청을 (어떻게이 그림의 명확한 설명이 Connector)가 들어오는 요청 Engine컨테이너 Engine파이프를 통해 ( Pieline(에 밸브를) Valve체인 호출에 대한) 최종 basic밸브에 대한 책임 용기의 제 1 밸브에 대한 다음 호출은 항상 호출을 Wrapper하고 Wrapper재실행 Servlet.

우리는 한 번 봐 걸릴 Wrappertrue의 경우 우리가 말했듯이, 소스 코드를 :

//源码8.
 public final void invoke(Request request, Response response)
        throws IOException, ServletException {
            //省略部分源码
        Servlet servlet = null;
        if (!unavailable) {
            servlet = wrapper.allocate();
        }
            
        // Create the filter chain for this request
        ApplicationFilterChain filterChain =
                ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);
                
         filterChain.doFilter(request.getRequest(),
                                    response.getResponse());        
        }
复制代码

당신은 분명 그냥 필터를 (만들어 여기 말할 수있다, 여기 참조 Filter)과 전화를하고 전화하지 Servlet, 그래, 정말이 전화를하지 않습니다 Servlet,하지만 우리는 필터 (알고 Filter)입니다 Servlet전에 실행, 즉, 인 filterChain.doFilter변화를 실행 한 후에 실행된다 Servlet. 우리는보고 ApplicationFilterChain우리가 말한대로, 여부 소스 코드 :

//源码9.
 public void doFilter(ServletRequest request, ServletResponse response)
        throws IOException, ServletException {
        //省略部分代码
        internalDoFilter(request,response);
    }
//源码10.  
 private void internalDoFilter(ServletRequest request,
                                  ServletResponse response)
        throws IOException, ServletException {
        //省略部分代码
        // Call the next filter if there is one
        if (pos < n) {
         //省略部分代码
            ApplicationFilterConfig filterConfig = filters[pos++];
            Filter filter = filterConfig.getFilter();
            filter.doFilter(request, response, this);
            return;
        }
        //调用servlet
        // We fell off the end of the chain -- call the servlet instance
        servlet.service(request, response);
        
         
复制代码

소스에 의해 우리는 (모든 필터를 호출 완료에있는 Filter이상), servlet전화를하기 시작했다 service. 우리는보고 servlet구현 클래스

여기에서 우리는 익숙 HttpServlet하고 GenericServlet있는 Tomcat사실, 단지 클래스의 패키지 HttpServlet때문에 부모입니다. 뒷면은 이미 완료되어이 내부 톰캣에 대한 요청을 처리 할 수있는 프레임 워크에 인도된다.GenericServletHttpServlet

다중 응용 프로그램 격리 톰캣 구현

우리는 톰캣의 여러 응용 프로그램의 배포를 지원하는 방법입니다 Tomcat이 여러 응용 프로그램의 배포를 지원, 알아? 어떻게의 여러 응용 프로그램 사이의 혼동이 없도록하는 것입니다? 이 문제를 이해하기 위해서, 우리는 여전히 어댑터, 다시 얘기로 돌아 가야 service방법

//源码11.类:CoyoteAdapter
public void service(org.apache.coyote.Request req, org.apache.coyote.Response res)
            throws Exception {
            //省略部分代码
            // Parse and set Catalina and configuration specific
            // request parameters
            //处理URL映射
            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);
            }
}
复制代码

우리는 소스에 대해 이야기하기 전에 connector.getService().getContainer().getPipeline().getFirst().invoke( request, response)코드이 코드는 선박을 호출하는 것입니다,하지만 컨테이너는 호출하기 전에이 postParseRequest, 우리는 소스에서보기를 따라 매핑 요청을 처리하는 데 사용되는 방법 :

//源码12.类:CoyoteAdapter
 protected boolean postParseRequest(org.apache.coyote.Request req, Request request,
            org.apache.coyote.Response res, Response response) throws IOException, ServletException {
        省略部分代码
        boolean mapRequired = true;
         while (mapRequired) {
            // This will map the the latest version by default
            connector.getService().getMapper().map(serverName, decodedURI,
                    version, request.getMappingData());
            //没有找到上下文就报404错误        
            if (request.getContext() == null) {
                // Don't overwrite an existing error
                if (!response.isError()) {
                    response.sendError(404, "Not found");
                }
                // Allow processing to continue.
                // If present, the error reporting valve will provide a response
                // body.
                return true;
            }        
            }
复制代码

여기 경우, URL 매핑을 처리하는 순환되어 Context찾을 수없는, 그것은 404 오류를 반환, 우리는 소스 코드를 보면 계속 :

//源码13.类:Mapper
public void map(MessageBytes host, MessageBytes uri, String version,
                    MappingData mappingData) throws IOException {

        if (host.isNull()) {
            String defaultHostName = this.defaultHostName;
            if (defaultHostName == null) {
                return;
            }
            host.getCharChunk().append(defaultHostName);
        }
        host.toChars();
        uri.toChars();
        internalMap(host.getCharChunk(), uri.getCharChunk(), version, mappingData);
    }
    //源码14.类:Mapper
 private final void internalMap(CharChunk host, CharChunk uri,
            String version, MappingData mappingData) throws IOException {
        //省略部分代码
        // Virtual host mapping 处理Host映射
        MappedHost[] hosts = this.hosts;
        MappedHost mappedHost = exactFindIgnoreCase(hosts, host);
      
         //省略部分代码
        if (mappedHost == null) {
             mappedHost = defaultHost;
            if (mappedHost == null) {
                return;
            }
        }
    
        mappingData.host = mappedHost.object;
        
        // Context mapping 处理上下文映射
        ContextList contextList = mappedHost.contextList;
        MappedContext[] contexts = contextList.contexts;
        //省略部分代码
        if (context == null) {
            return;
        }
        mappingData.context = contextVersion.object;
        mappingData.contextSlashCount = contextVersion.slashCount;

        // Wrapper mapping 处理Servlet映射
        if (!contextVersion.isPaused()) {
            internalMapWrapper(contextVersion, uri, mappingData);
        }

    }    
复制代码

더 위의 코드, 나는, 많은 코드를 생략 일반적으로 코드를 이해할 수있는 주요 논리는 URL이 세 부분으로 매핑 구성을 처리하는 것입니다 유지 Host, 매핑 Context및 매핑 Servlet, 제발 (관심 소스의 특정 세부 사항을 공간을 절약하기 위해 학생들은) 연구를 소유하고 있습니다.

여기에서 우리는 밀접하게 세 개의 처리 로직에 관련된 세부 사항을 찾을 만 할 수 Host없는 처리하기 전에 빈 Context에 대한 Servlet같은 이유입니다. 그래서 여기에 우리가 Host모든 후속 하위 선박이 다른, 다르게 구성, 그것은 응용 프로그램 격리 효과를 완료했다. 그러나 SpringBoot에 대한 (항아리 패키지 시작을 사용하여) 톰캣 모드를 내장, 그것은 다중 응용 프로그램 모드가없는, 그 자체가 응용 프로그램은 톰캣이다.

이해의 편의를 위해, 나는이 두 개의 도메인 이름 것을 우리가 가정 다중 응용 프로그램 격리의 다이어그램을 그려 admin.luozhou.comweb.luozhou.com각각 다음 내 도메인의 각 두 개의 응용 프로그램을 배포 User, log, blog, shop. 내가 사용자를 추가 할 때, 내가 요청합니다 admin.luozhou.com도메인 이름 UserContext다음 add서블릿을 ( 참고 :이 예제 디자인은 실제 개발 원칙에 부합하지 않는, 완료 프레임 워크 컨트롤러해야이 크기를 추가하는 대신 서블릿 ).

개요

이 문서에서 우리는 Tomcat 컨테이너는 요청을 처리의 콘텐츠에 되돌아 볼 수 있도록하는 방법입니다 연구 :

  • 요청 던진 적합한 용기 커넥터 어댑터를 호출 한 후 ( Engine)
  • 파이프 (통해 상기 용기의 내부 Pieline) - 밸브 ( Valve) 호출 모드는 용기 주 통해 상위 서브 탱크 호출 용기 완료된 basic완료 밸브.
  • 마지막 하위 컨테이너 wrapper호출 후에는 통화가 톰캣 내부의 마지막 단계를 완료 한 후, 전화를 필터링하는 필터를 구축 서블릿을 호출이 완료됩니다. 우리는 또한 일반적인 이해할 수있는 HttpServlet모든 기반 Servlet프레임 워크 사양 (SpringBoot 포함) 프레임 프로세스를 입력 여기.
  • 마지막으로, 우리는 또한 Tomcat이 다중 응용 프로그램 격리 분석을 통해 다중 응용 프로그램 격리, 어떻게 조사, 우리는 또한 Tomcat이 많은 하위 컨테이너를 설계하는 등 하위 컨테이너가 다른 달성하는 데 필요한 격리 수준의 다른 단위에서 할 수있는 이유를 이해 장면이 필요합니다.

저작권 : 원래 기사, 소스를 표시하시기 바랍니다.

추천

출처juejin.im/post/5d63d822f265da03cb1247a0