![](https://cdn.pixabay.com/photo/2014/07/10/17/18/battleship-389274_1280.jpg)
머리말
이전 " 커넥터 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);
}
复制代码
위의 코드는 우리가 알고있는 basic
A (파이프 인 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
.
우리는 한 번 봐 걸릴 Wrapper
true의 경우 우리가 말했듯이, 소스 코드를 :
//源码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
때문에 부모입니다. 뒷면은 이미 완료되어이 내부 톰캣에 대한 요청을 처리 할 수있는 프레임 워크에 인도된다.GenericServlet
HttpServlet
다중 응용 프로그램 격리 톰캣 구현
우리는 톰캣의 여러 응용 프로그램의 배포를 지원하는 방법입니다 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.com
및 web.luozhou.com
각각 다음 내 도메인의 각 두 개의 응용 프로그램을 배포 User
, log
, blog
, shop
. 내가 사용자를 추가 할 때, 내가 요청합니다 admin.luozhou.com
도메인 이름 User
의 Context
다음 add
서블릿을 ( 참고 :이 예제 디자인은 실제 개발 원칙에 부합하지 않는, 완료 프레임 워크 컨트롤러해야이 크기를 추가하는 대신 서블릿 ).
개요
이 문서에서 우리는 Tomcat 컨테이너는 요청을 처리의 콘텐츠에 되돌아 볼 수 있도록하는 방법입니다 연구 :
- 요청 던진 적합한 용기 커넥터 어댑터를 호출 한 후 (
Engine
) - 파이프 (통해 상기 용기의 내부
Pieline
) - 밸브 (Valve
) 호출 모드는 용기 주 통해 상위 서브 탱크 호출 용기 완료된basic
완료 밸브. - 마지막 하위 컨테이너
wrapper
호출 후에는 통화가 톰캣 내부의 마지막 단계를 완료 한 후, 전화를 필터링하는 필터를 구축 서블릿을 호출이 완료됩니다. 우리는 또한 일반적인 이해할 수있는HttpServlet
모든 기반Servlet
프레임 워크 사양 (SpringBoot 포함) 프레임 프로세스를 입력 여기. - 마지막으로, 우리는 또한 Tomcat이 다중 응용 프로그램 격리 분석을 통해 다중 응용 프로그램 격리, 어떻게 조사, 우리는 또한 Tomcat이 많은 하위 컨테이너를 설계하는 등 하위 컨테이너가 다른 달성하는 데 필요한 격리 수준의 다른 단위에서 할 수있는 이유를 이해 장면이 필요합니다.
저작권 : 원래 기사, 소스를 표시하시기 바랍니다.