tomcat源码解析(五)--两种配置与异步servlet

来源:互联网 发布:南昌大学怎么样知乎 编辑:程序博客网 时间:2024/06/06 01:41

常用的web基本都是用xml配置的,而在tomcat解析web.xml的过程中知道,如果类实现了ServletContainerInitializer接口的话,就可以不用在xml里面配置了.
现在对下面两种方式进行说明:
首先是xml配置,web.xml的代码如下:

<?xml version="1.0" encoding="UTF-8"?><web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee         http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"         version="3.1">    <servlet>        <servlet-name>testservlet</servlet-name>        <servlet-class>com.yluo.TestServlet</servlet-class>    </servlet>    <servlet-mapping>        <servlet-name>testservlet</servlet-name>        <url-pattern>/testservlet</url-pattern>    </servlet-mapping></web-app>

这个配置大家都很熟悉的吧.
下面看到实现了ServletContainerInitializer接口的配置,代码如下:

package com.yluo.config;import com.yluo.servlet.TestServlet;import javax.servlet.ServletContainerInitializer;import javax.servlet.ServletContext;import javax.servlet.ServletException;import javax.servlet.ServletRegistration;import java.util.Set;/** * Created by 樱天寻 on 2016/7/13. */public class WebConfig implements ServletContainerInitializer {    @Override    public void onStartup(Set<Class<?>> set, ServletContext servletContext) throws ServletException {        ServletRegistration.Dynamic dynamic = servletContext.addServlet("testServler", TestServlet.class);        dynamic.setLoadOnStartup(1);        dynamic.addMapping("/testServler");    }}

不过在这里有点不一样的就是要添加下面这么一个文件
这里写图片描述
WEB-INF/classes/META-INF/services/javax.servlet.ServletContainerInitializer文件的内容如下:

com.yluo.config.WebConfig

就是把实现ServletContainerInitializer接口的类填上.这样就不用配置xml了,是不是很方便呢.

下面分析异步servlet.示例代码如下:

@Override    protected void doGet(HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException {        final AsyncContext asyncContext = req.startAsync();        new Thread(new Runnable() {            @Override            public void run() {                try {                    resp.getWriter().write("hello world!");                } catch (IOException e) {                    e.printStackTrace();                }            }        }).start();    }

异步的话一般都是在新的线程的进行的,这样才不会把任务阻塞在tomcat内部的哪个线程池里面.开启异步之后改方法就返回了,tomcat做了哪些工作呢.在上一章中有说到,请求路径的匹配是在org.apache.coyote.http11.Http11Processor类的service里面的,那么看到该方法,部分代码如下:

 @Override    public SocketState service(SocketWrapperBase<?> socketWrapper)        throws IOException {            ......            getAdapter().service(request, response); //              ......              if (!isAsync()) {                // 不开启异步的正常关闭                endRequest();            }            ......        if (getErrorState().isError() || endpoint.isPaused()) {            return SocketState.CLOSED;        } else if (isAsync()) {            // 异步就返回SocketState.LONG;            return SocketState.LONG;        }        ......    }

在servlet的doGet方法返回之后,就是回到该方法判是不是异步了,接着如果是异步的话service函数就返回 SocketState.LONG 继续往前看,看到org.apache.coyote.AbstractProtocol类的process方法,部分代码如下:

    @Override         public SocketState process(SocketWrapperBase<S> wrapper, SocketEvent status) {            ......            state = processor.process(wrapper, status);            try{                if (state == SocketState.LONG) {                    longPoll(wrapper, processor); // 第一次连接的时候就是在这里处理了                    if (processor.isAsync()) { // 返回的还是这里异步的                        // 添加到定时器里面定时检测是偶过期                        getProtocol().addWaitingProcessor(processor);                    }                }                 ......                return state; // 返回LONG            } catch(java.net.SocketException e) {                ......            }             ......        }

processor.process发挥的state值为SocketState.LONG,在这里会判断是否开启了异步,如果开始了执行addWaitingProcessor方法,部分代码如下:

 public void addWaitingProcessor(Processor processor) {        waitingProcessors.add(processor);    }

而waitingProcessors是一个Set集合

private final Set<Processor> waitingProcessors =            Collections.newSetFromMap(new ConcurrentHashMap<Processor, Boolean>());

加进来有什么用呢,看到AbstractProtocol类的子类AsyncTimeout的run方法的代码如下:

   @Override        public void run() {            while (asyncTimeoutRunning) {                try {                    Thread.sleep(1000);                } catch (InterruptedException e) {                }                long now = System.currentTimeMillis();                for (Processor processor : waitingProcessors) {                   processor.timeoutAsync(now);                }                while (endpoint.isPaused() && asyncTimeoutRunning) {                    try {                        Thread.sleep(1000);                    } catch (InterruptedException e) {                        // Ignore                    }                }            }        }

从该方法可以知道,tomcat会一个子线程里面每隔一秒检测异步请求有没有结束或者超时.

O啦,tomcat的源码就分析到这里了. 第一次写博客分析源码,有很多不足之处,希望大家见谅吧. 后面就是分析netty的源码了. 如果大家有什么问题可以给我留言哈.

1 0
原创粉丝点击