Tomcat启动过程(二):EndPoint解析

EndPoint提供基础的网络IO服务,用来实现网络连接和控制,它是服务器对外I/O操作的接入点。主要任务是管理对外的socket连接,同时将建立好的socket连接交到合适的工作线程中去。
里面两个主要的属性类是Acceptor和Poller、SocketProcessor

Acceptor

Acceptor类实现了Runnable接口,主要用于接收网络请求,建立连接,连接建立之后,将一个SocketChannel对象包装成一个NioChannel,并注册到Poller中。由Poller来负责执行数据的读取和业务执行。

我们看一下Acceptor的run方法:

public void run() {
     SocketChannel socket = serverSock.accept();//从监听的serversocket中获取新的连接
     setSocketOptions(socket);//设置通道的属性
     ……
}
protected boolean setSocketOptions(SocketChannel socket) {
    NioChannel = channel = new NioChannel(socket, bufhandler);//将通道包装成NioChannel
    getPoller0().register(channel);//从poller数组中选择一个poller,将channel注册到poller中
    ……
}

Poller

Poller实现了Runnable接口,在NioEndpoint的时候,会初始化pollers数组,同时启动pollers数组中的线程,让pollers开始工作。
封装后socketchannel的放入Poller线程内部维护的一个PollerEvent队列中,然后在Poller线程运行时处理队列,将socketchannel注册到这个Poller的Selector上。
当事件到来的时候,Selector发现要处理的事件,通过selector.select系列方法来获取数据,然后经由processKey到processSocket方法,封装成一个SocketProcessor对象后,放在EndPoint的线程池中执行。
SocketChannel是如何注册到Poller中的?

protected ConcurrentLinkedQueue<Runnable> events = new ConcurrentLinkedQueue<Runnable>();//内部维护的事件队列
public void register(final NioChannel socket)
{
    socket.setPoller(this);
    KeyAttachment key = keyCache.poll();
    final KeyAttachment ka = key!=null?key:new KeyAttachment();
    ka.reset(this,socket,getSocketProperties().getSoTimeout());
    PollerEvent r = eventCache.poll();
    ka.interestOps(SelectionKey.OP_READ);//this is what OP_REGISTER turns into.
    if ( r==null) r = new PollerEvent(socket,ka,OP_REGISTER);
    else r.reset(socket,ka,OP_REGISTER);
    addEvent(r);//socketchannel、key一同加入到了events队列
}

SocketChannel是如何注册到每个Poller的selector中的?答案在event()方法中,在该方法中遍历events队列,依次执行run方法

public boolean events() {
    while ( (r = (Runnable)events.poll()) != null ) {
        result = true;
        r.run();
    }
    ……
}
public void run() {
    if ( interestOps == OP_REGISTER ) {
        socket.getIOChannel().register(socket.getPoller().getSelector(), SelectionKey.OP_READ, key);//注册到selector中
    }
    else {
        final SelectionKey key = socket.getIOChannel().keyFor(socket.getPoller().getSelector());
        int ops = key.interestOps() | interestOps;
        att.interestOps(ops);
        key.interestOps(ops);
        att.setCometOps(ops);//另外一种注册方法?
    }   ……

}

Poller的执行在其run方法中,主要是将请求封装成SocketProcessor对象,交给线程池处理。

public void run() {
    if ( keyCount == 0 ) hasEvents = (hasEvents | events());//通过事件机制监控感兴趣的网络事件,见上面的events()分析
    //遍历渠道上到来的key,交给processor去处理
    Iterator iterator = keyCount > 0 ? selector.selectedKeys().iterator() : null;
    for()
    {
        SelectionKey sk = (SelectionKey) iterator.next();
        KeyAttachment attachment = (KeyAttachment)sk.attachment();
        processKey(sk, attachment);
    }
}//processKey(sk, attachment)调用processSocket(channel, SocketStatus.OPEN),最终使用Endpoint的线程池执行请求protected boolean processSocket(NioChannel socket, SocketStatus status, boolean dispatch) {
    KeyAttachment attachment = (KeyAttachment)socket.getAttachment(false);
    attachment.setCometNotify(false); //will get reset upon next reg
    sc = new SocketProcessor(socket,status);
    if ( dispatch ) executor.execute(sc);=====>任务被封装成SocketProcessor对象,在成功获取线程池后,则通过线程池来进行socket数据数据的读写操作。
    else sc.run();
    ……
} 

SocketProcessor

请求到达socketProcess之后,首先执行其run方法,请求被转移到handler.process,根据上下文,我们知道这了的hander指的是Http11ConnectionHandler

public void run() {
    (status==null)?(handler.process(socket)==Handler.SocketState.CLOSED) :
                    (handler.event(socket,status)==Handler.SocketState.CLOSED);
}

参考文献:

http://blog.csdn.net/yanlinwang/

时间: 2024-08-12 20:03:22

Tomcat启动过程(二):EndPoint解析的相关文章

Tomcat架构解析(二)-----Connector、Tomcat启动过程以及Server的创建过程

Connector用于跟客户端建立连接,获取客户端的Socket,交由Container处理.需要解决的问题有监听.协议以及处理器映射等等. 一.Connector设计   Connector要实现的主要功能如下: 设计图如下: 1.ProtocolHandler Connector中的ProtocolHandler用于处理不同的通信协议,Tomcat主要支持HTTP.AJP协议,并且支持BIO.NIO.APR等I/O方式.ProtocolHandler中使用AbstractEndpoint启动

Tomcat启动过程原理详解 -- 非常的报错:涉及了2个web.xml等文件的加载流程

Tomcat启动过程原理详解 发表于: Tomcat, Web Server, 旧文存档 | 作者: 谋万世全局者 标签: Tomcat,原理,启动过程,详解 基于Java的Web 应用程序是 servlet.JSP 页面.静态页面.类和其他资源的集合,它们可以用标准方式打包,并运行在来自多个供应商的多个容器(诸如tomcat).Web 应用程序存在于结构化层次结构的目录中,该层次结构是由 Java Servlet 规范定义的.Web 应用程序的根目录包含直接存储或存储在子文件夹中的所有公共资源

Tomcat探秘(4):tomcat启动过程详述

熟悉Tomcat的工程师们,或者从事Java开发的,肯定都知道Tomcat是如何启动和停止的.在Tomcat源码包里面有个bin目录,该目录下放置了一些很重要的脚本,Tomcat启动和停止的脚本程序就放在这里,分别是startup.bat.shutdown.bat(Windows环境)和start.sh.shutdown.sh(Linux.Unix环境).大家一定都知道如何使用它们,接下来就是研究一下它们是如何实现启动和停止服务的. 在实际的生产环境下,绝大多数的Tomcat都是部署在Linux

tomcat启动过程报the JDBC Driver has been forcibly unregistered问题的修复过程

最近两天在整理关于flume的总结文档,没有启动过tomcat.昨天晚上部署启动,发现报了如题的错误,全文如下: 严重: The web application [/oa-deploy] registered the JBDC driver [com.microsoft.sqlserver.jdbc.SQLServerDriver] but failed to unregister it when the web application was stopped. To prevent a mem

导致Tomcat启动过程缓慢的原因及解决方法

1.现象 在CentOS启动Tomcat时,启动过程很慢,需要几分钟,经过查看日志,发现耗时在这里:是session引起的随机数问题导致的.Tocmat的Session ID是通过SHA1算法计算得到的, 计算Session ID的时候必须有一个密钥.为了提高安全性Tomcat在启动的时候会通过随机生成一个密钥. 22-Apr-2017 19:33:07.623 INFO [localhost-startStop-1] org.apache.catalina.util.SessionIdGene

转:Tomcat启动过程中找不到JAVA_HOME JRE_HOME的解决方法

转自:http://blog.sina.com.cn/s/blog_61c006ea0100l1u6.html 原文: 在XP上明明已经安装了JDK1.5并设置好了JAVA_HOME,可偏偏Tomcat在启动过程中找不到.    报错信息如下:Neither the JAVA_HOME nor the JRE_HOME environment variable is defined At least one of these environment variable is needed to r

Tomcat 启动过程概览

可以使用bin目录下的脚本启动.在代码里,启动的入口是类Bootstrap的main方法. 可以看tomcat官网的启动过程PDF 对于"start"的命令.经过了init.load.start三个方法调用. init 用来初始化以下ClassLoader(在Tomcat-5.5版本后,有实际意义的只有commonLoader了). commonLoader catalinaLoader sharedLoader commonLoader是catalinaLoader和sharedLo

Tomcat启动过程原理详解

基于Java的Web 应用程序是 servlet.JSP 页面.静态页面.类和其他资源的集合,它们可以用标准方式打包,并运行在来自多个供应商的多个容器.Web 应用程序存在于结构化层次结构的目录中,该层次结构是由 Java Servlet 规范定义的.Web 应用程序的根目录包含直接存储或存储在子文件夹中的所有公共资源,比如图像.HTML 页面等.构成:Web应用由Web组件(一组Java类库).html文件,静态资源文件(如图像).帮助类和库组成. 1 – Tomcat Server的组成部分

Tomcat启动过程

绑定端口及接收socket请求 Connector.setProtocol是在解析xml配置文件时绑定关系, 启动Connector initInternal() ===>protocolHandler.init() ==> Endpoint.init()-->Endpoint.bind() Endpoint.startInternal() 原文地址:https://www.cnblogs.com/yszzu/p/9261117.html