Tomcat启动过程(番外1--怎样接受连接)

   我先整个看下与连接部分相关的结构:

              通过前面的介绍,是由server.xml通过Digester去创建整个容器的结构的,我们再回顾整理下前面的内容。createStartDigester方法里面的一些内容(这里创建Connector并将其addConnector(加到StandardService中)): 

digester.addRule("Server/Service/Connector",
                 new ConnectorCreateRule());
digester.addRule("Server/Service/Connector", new SetAllPropertiesRule(
        new String[]{"executor", "sslImplementationName", "protocol"}));
digester.addSetNext("Server/Service/Connector",
                    "addConnector",
                    "org.apache.catalina.connector.Connector");

 对应server.xml文件:

<Service name="Catalina">

       <Connector port="8080" protocol="HTTP/1.1"  connectionTimeout="20000" redirectPort="8443" />

这里是通过ConnectorCreateRule类去解析创建Connector对象,并将其添加到Service(StandardService)的,我们看下ConnectorCreateRule类是怎样去创建Connector的:

@Override
public void begin(String namespace, String name, Attributes attributes)
        throws Exception {
    Service svc = (Service)digester.peek();
    Executor ex = null;
    if ( attributes.getValue("executor")!=null ) {
        ex = svc.getExecutor(attributes.getValue("executor"));
    }
    Connector con = new Connector(attributes.getValue("protocol"));
    if (ex != null) {
        setExecutor(con, ex);
    }
    String sslImplementationName = attributes.getValue("sslImplementationName");
    if (sslImplementationName != null) {
        setSSLImplementationName(con, sslImplementationName);
    }
    digester.push(con);
}

通过new Connector(attributes.getValue("protocol")):看下其构造方法:

public Connector(String protocol) {
    boolean aprConnector = AprLifecycleListener.isAprAvailable() &&
            AprLifecycleListener.getUseAprConnector();
    if ("HTTP/1.1".equals(protocol) || protocol == null) {
        if (aprConnector) {
            protocolHandlerClassName = "org.apache.coyote.http11.Http11AprProtocol";
        } else {
            protocolHandlerClassName = "org.apache.coyote.http11.Http11NioProtocol";
        }
    } else if ("AJP/1.3".equals(protocol)) {
        if (aprConnector) {
            protocolHandlerClassName = "org.apache.coyote.ajp.AjpAprProtocol";
        } else {
            protocolHandlerClassName = "org.apache.coyote.ajp.AjpNioProtocol";
        }
    } else {
        protocolHandlerClassName = protocol;
    }
    ProtocolHandler p = null;
    try {
        Class<?> clazz = Class.forName(protocolHandlerClassName);
        p = (ProtocolHandler) clazz.getConstructor().newInstance();
    } ......
     finally {
        this.protocolHandler = p;
    }
}

这里的协议是HTTP/1.1,所以Connector的protocolHandler变量是Http11AprProtocol。并且与连接相关的NioEndpoint是Connector的成员变量、而Poller、Accpetor又是NioEndPoint的内部类。所以一个socket能找到连接器Connector,然后通过Connector找到其StandardService。由此一个socket连接想向上能找到StandardServer、向下也能找到Engine、Host等这些容器来处理这个socket(这些容器的内容以及通过上一章讲过的mapperListener.start(),设置到了StandardService的Mapper中了),现在我们来看下具体的过程。

         1、首先是Accptor接受然后通过Poller去处理,转换为TaskThread,这里就不具体再描叙了是上一章就说过了,这里我们直接从 getAdapter().service(request, response)开始,关于这两个入参的初始化这篇有提到:

@Override
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);

    if (request == null) {
        // Create objects
        request = connector.createRequest();
        request.setCoyoteRequest(req);
        response = connector.createResponse();
        response.setCoyoteResponse(res);

        // Link objects
        request.setResponse(response);
        response.setRequest(request);

        // Set as notes
        req.setNote(ADAPTER_NOTES, request);
        res.setNote(ADAPTER_NOTES, response);

        // Set query string encoding
        req.getParameters().setQueryStringCharset(connector.getURICharset());
    }
          ........
        postParseSuccess = postParseRequest(req, request, res, response);
        if (postParseSuccess) {
            request.setAsyncSupported(
                    connector.getService().getContainer().getPipeline().isAsyncSupported());
            // Calling the container
            connector.getService().getContainer().getPipeline().getFirst().invoke(
                    request, response);
         ..............

    } 
             ...........
    }
}

这里两种Request、Response在文章中也有提到就不在描叙了。首先取不到,通过connector创建:

public Request(Connector connector) {
    this.connector = connector;
      .........
}

将connector设置到这个Request中,之后request.setCoyoteRequest(req)(调用request的方法,其实request首次是再调用req的方法,然后将结果再放到request的成员变量中)。

 之后是postParseRequest(req, request, res, response)方法:

protected boolean postParseRequest(org.apache.coyote.Request req, Request request,
        org.apache.coyote.Response res, Response response) throws IOException, ServletException {

         ............

     parsePathParameters(req, request);

         .............

     connector.getService().getMapper().map(serverName, decodedURI, version, request.getMappingData());

      ..............

  MessageBytes redirectPathMB = request.getMappingData().redirectPath;
     if (!redirectPathMB.isNull()) {
         String redirectPath = URLEncoder.DEFAULT.encode(
                 redirectPathMB.toString(), StandardCharsets.UTF_8);
           ........
          response.sendRedirect(redirectPath);
        .....
 }

   doConnectorAuthenticationAuthorization(req,request);

}

  通过parsePathParameters(req, request)方法解析路径参数(从req URL中带;的参数,一般没有看到这种用法)并设置到request中。

   之后是connector.getService().getMapper().map(serverName, decodedURI, version, request.getMappingData())方法(这个方法很关键):

    我们看下方法其的整个调用链。通过connector获取StandardService,再获取Mapper(就是前面由mapperListener.start()设置到StandardService中的),调用其map方法,入参是有request的成员变量MappingData,其变量有:

public class MappingData {

    public Host host = null;
    public Context context = null;
    public int contextSlashCount = 0;
    public Context[] contexts = null;
    public Wrapper wrapper = null;
    public boolean jspWildCard = false;

    public final MessageBytes contextPath = MessageBytes.newInstance();
    public final MessageBytes requestPath = MessageBytes.newInstance();
    public final MessageBytes wrapperPath = MessageBytes.newInstance();
    public final MessageBytes pathInfo = MessageBytes.newInstance();

    public final MessageBytes redirectPath = MessageBytes.newInstance();

    // Fields used by ApplicationMapping to implement javax.servlet.http.Mapping
    public MappingMatch matchType = null;

}

看下Mapper的map方法(参数内容):

    

map方法调用internalMap方法:
private final void internalMap(CharChunk host, CharChunk uri,
                               String version, MappingData mappingData) throws IOException {
    // Virtual host mapping
    Mapper.MappedHost[] hosts = this.hosts;
    Mapper.MappedHost mappedHost = exactFindIgnoreCase(hosts, host);
    ..........
    mappingData.host = mappedHost.object;
     ..........
    // Context mapping
    Mapper.ContextList contextList = mappedHost.contextList;
    Mapper.MappedContext[] contexts = contextList.contexts;
    int pos = find(contexts, uri);
    ...........
    Mapper.MappedContext context = null;
    while (pos >= 0) {
        context = contexts[pos];
        ............
        pos = find(contexts, uri);
    }
    .........
            context = contexts[0];
    ..........

    mappingData.contextPath.setString(context.name);
    Mapper.ContextVersion contextVersion = null;
    Mapper.ContextVersion[] contextVersions = context.versions;
    final int versionCount = contextVersions.length;
    if (versionCount > 1) {
        Context[] contextObjects = new Context[contextVersions.length];
        for (int i = 0; i < contextObjects.length; i++) {
            contextObjects[i] = contextVersions[i].object;
        }
        mappingData.contexts = contextObjects;
        if (version != null) {
            contextVersion = exactFind(contextVersions, version);
        }
    }
    if (contextVersion == null) {
        contextVersion = contextVersions[versionCount - 1];
    }
    mappingData.context = contextVersion.object;
    mappingData.contextSlashCount = contextVersion.slashCount;
    // Wrapper mapping
    if (!contextVersion.isPaused()) {
        internalMapWrapper(contextVersion, uri, mappingData);
    }
}

       1、首先通过CharChunk host,找到Mapper.MappedHost,并设置到mappingData中,再之后通过CharChunk uri找到对应的MappedContext,然后找到contextVersions,这里与contextVersions相关的概念有个webappVersion属性,对应在contextVersion中就是那么属性,默认是"",现在还不明白这个设计的目的:

    

         最后通过这个contextversion找到context:mappingData.context = contextVersion.object。

         之后调用internalMapWrapper方法(通过这个方法找到Wrapper(即对应的servlet处理类)):

private final void internalMapWrapper(ContextVersion contextVersion,
                                      CharChunk path,
                                      MappingData mappingData) throws IOException {
         .....
    MappedWrapper[] exactWrappers = contextVersion.exactWrappers;
    internalMapExactWrapper(exactWrappers, path, mappingData);
       ........

  }

现在的入参是:这里注意path已经是"/ts"了说明在这里是要找Wrapper了,同时mappingData有两个属性contextPath(Context的路径)、requestPath(Wrapper的路径):

  internalMapExactWrapper方法:

private final void internalMapExactWrapper
    (MappedWrapper[] wrappers, CharChunk path, MappingData mappingData) {
    MappedWrapper wrapper = exactFind(wrappers, path);
    if (wrapper != null) {
        mappingData.requestPath.setString(wrapper.name);
        mappingData.wrapper = wrapper.object;
        if (path.equals("/")) {
            // Special handling for Context Root mapped servlet
            mappingData.pathInfo.setString("/");
            mappingData.wrapperPath.setString("");
            // This seems wrong but it is what the spec says...
            mappingData.contextPath.setString("");
            mappingData.matchType = MappingMatch.CONTEXT_ROOT;
        } else {
            mappingData.wrapperPath.setString(wrapper.name);
            mappingData.matchType = MappingMatch.EXACT;
        }
    }
}

通过path找到对应的wrapper,并设置到mappingData中:

   

             至此就通过请求的url以及原来的Mapper找到了对应的host-》context-》wrapper(Servlet)  。现在再回到原来的

方法postParseRequest,之后是如果有重定向,则将重定向设置到response中:
MessageBytes redirectPathMB = request.getMappingData().redirectPath;
if (!redirectPathMB.isNull()) {
    String redirectPath = URLEncoder.DEFAULT.encode(
            redirectPathMB.toString(), StandardCharsets.UTF_8);
    ........
    response.sendRedirect(redirectPath);
   .....
}

   然后是doConnectorAuthenticationAuthorization方法,这个是授权方法,先不管这个。

    所以这里通过一连串的调用,最终是将standardService中的Mapper的关于容器的内容设置到了Request中的mappingData中。下面就是这个mappingData的使用了。

     现在我们再将目光移到最开始的service方法,接下来是调用connector.getService().getContainer().getPipeline().getFirst().invoke( request, response)方法:

     这里就是pipeline在四个容器的调用了:我们先通过最初的StandardEngineValve来看下mappingData的使用:

       

 

@Override
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);
}

      

看下request.getHost()方法(就是通过mappingData一步步获取对应的host、context、wrapper):

public Host getHost() {
    return mappingData.host;
}

这里最终会调到StandardWrapperValve,看其invoke方法:

@Override
public final void invoke(Request request, Response response)
        throws IOException, ServletException {
    ...........
    StandardWrapper wrapper = (StandardWrapper) getContainer();
    Servlet servlet = null;
    Context context = (Context) wrapper.getParent();
     ...........
            servlet = wrapper.allocate();
    ...........
    ApplicationFilterChain filterChain =
            ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);
        if ((servlet != null) && (filterChain != null)) {
            // Swallow output if needed
            if (context.getSwallowOutput()) {
                   ......
                        filterChain.doFilter(request.getRequest(),
                                response.getResponse());
                } 
                ........
            } else {
                    filterChain.doFilter
                            (request.getRequest(), response.getResponse());
                }
            }
        }
   ........
}

     1、获取到StandardWrapper。

     2、通过wrapper去初始化获取servlet,wrapper.allocate()。

     3、创建ApplicationFilterChain执行链,ApplicationFilterFactory.createFilterChain(request, wrapper, servlet):

         

public static ApplicationFilterChain createFilterChain(ServletRequest request,
                                                       Wrapper wrapper, Servlet servlet) {
    ..........
    // Create and initialize a filter chain object
    ApplicationFilterChain filterChain = null;
    if (request instanceof Request) {
            ........
            filterChain = (ApplicationFilterChain) req.getFilterChain();
            if (filterChain == null) {
                filterChain = new ApplicationFilterChain();
                req.setFilterChain(filterChain);
        }
    }
    ..........
    filterChain.setServlet(servlet);
    // Acquire the filter mappings for this Context
    StandardContext context = (StandardContext) wrapper.getParent();
    FilterMap filterMaps[] = context.findFilterMaps();
    ..........
    // Acquire the information we will need to match filter mappings
    DispatcherType dispatcher =
            (DispatcherType) request.getAttribute(Globals.DISPATCHER_TYPE_ATTR);
    String requestPath = null;
    Object attribute = request.getAttribute(Globals.DISPATCHER_REQUEST_PATH_ATTR);
    if (attribute != null){
        requestPath = attribute.toString();
    }
    String servletName = wrapper.getName();
    // Add the relevant path-mapped filters to this filter chain
    for (int i = 0; i < filterMaps.length; i++) {
        if (!matchDispatcher(filterMaps[i] ,dispatcher)) {
            continue;
        }
        if (!matchFiltersURL(filterMaps[i], requestPath))
            continue;
        ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
                context.findFilterConfig(filterMaps[i].getFilterName());
        filterChain.addFilter(filterConfig);
    }
    // Add filters that match on servlet name second
    for (int i = 0; i < filterMaps.length; i++) {
        if (!matchDispatcher(filterMaps[i] ,dispatcher)) {
            continue;
        }
        if (!matchFiltersServlet(filterMaps[i], servletName))
            continue;
        ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
                context.findFilterConfig(filterMaps[i].getFilterName());
        filterChain.addFilter(filterConfig);
    }
    return filterChain;
}

   这里就是这个ApplicationFilterChain的创建,设置其的最终运行目标Servlet,然后获取context的过滤器Filter(一个context对应的就是一个web.xml文件,这个过滤器就是配置在其中的),将这些Filter添加到ApplicationFilterChain到。

然后是filterChain.doFilter(request.getRequest(), response.getResponse())方法的调用,doFilter方法调用的是internalDoFilter方法:

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);
            .............
           reuturn;
    }

      ...........
            servlet.service(request, response);
      ...........
}

这个结构可以清楚的看到,先调用所有的filter,如果其中有的Filter类最后没有再调用filterChain.doFilter (request, response)形成循环调用,将所有的Filter都运行完,则最后会return就不会运行Servlet了。当所有的Filter运行完后,就调用Servlet的service方法了。

    至此,这个请求的处理就完成了,下面补充关于Servlet接口的内容。

                            

    

     自己写的Servlet一般会继承HttpServlet类,再重写doGet/doPost方法(自己也可以直接继承Servlet,然后去自定义一些细节):

public class MyServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        response.setContentType("text/html");
        PrintWriter writer = response.getWriter();
        writer.println("<html>\n" +
                "  <head>\n" +
                "    <title>Title</title>\n" +
                "  </head>\n" +
                "  <body>\n" +
                "  Hello Servlet\n" +
                "  </body>\n" +
                "</html>");
        writer.flush();
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request,response);
    }
}

                      

   

          前面的servlet.service方法我们看下在HttpServlet中是怎样处理的:

protected void service(HttpServletRequest req, HttpServletResponse resp)
    throws ServletException, IOException {

    String method = req.getMethod();

    if (method.equals(METHOD_GET)) {
        long lastModified = getLastModified(req);
        if (lastModified == -1) {
            // servlet doesn't support if-modified-since, no reason
            // to go through further expensive logic
            doGet(req, resp);
        } else {
            long ifModifiedSince;
            try {
                ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
            } catch (IllegalArgumentException iae) {
                // Invalid date header - proceed as if none was set
                ifModifiedSince = -1;
            }
            if (ifModifiedSince < (lastModified / 1000 * 1000)) {
                // If the servlet mod time is later, call doGet()
                // Round down to the nearest second for a proper compare
                // A ifModifiedSince of -1 will always be less
                maybeSetLastModified(resp, lastModified);
                doGet(req, resp);
            } else {
                resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
            }
        }

    } else if (method.equals(METHOD_HEAD)) {
        long lastModified = getLastModified(req);
        maybeSetLastModified(resp, lastModified);
        doHead(req, resp);

    } else if (method.equals(METHOD_POST)) {
        doPost(req, resp);

    } else if (method.equals(METHOD_PUT)) {
        doPut(req, resp);

    } else if (method.equals(METHOD_DELETE)) {
        doDelete(req, resp);

    } else if (method.equals(METHOD_OPTIONS)) {
        doOptions(req,resp);

    } else if (method.equals(METHOD_TRACE)) {
        doTrace(req,resp);

    } else {
        //
        // Note that this means NO servlet supports whatever
        // method was requested, anywhere on this server.
        //

        String errMsg = lStrings.getString("http.method_not_implemented");
        Object[] errArgs = new Object[1];
        errArgs[0] = method;
        errMsg = MessageFormat.format(errMsg, errArgs);

        resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值