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

常用的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的源码了. 如果大家有什么问题可以给我留言哈.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值