tomcat如何建立连接,处理请求

学习探讨tomcat如何建立网络连接协议,并处理客户端过来的请求

建立http网络连接,指定通信协议

tomcat在创建时,会创建连接对象,负责处理客户端的请求,基于socket

connector 连接 protocol 协议 endpoint终端 socket插座,端口连接

创建初始化

connector -> protocol -> endpoint -> socket

接收请求创建任务

acceptor.socket.acceptor()->

​ socketWrapper(携带通信信息)

​ -> poller(socketWrapper)

​ -> execute(socketWrapper) 创建线程

创建连接器

Conector类

org.apache.catalina.connector.Connector

空参构造connector() -> connector(http/1.1)

/**
* Defaults to using HTTP/1.1 NIO implementation.
*/
public Connector() {
this("HTTP/1.1");
}

指定网络连接协议http11

org.apache.coyote.http11.Http11NioProtocol

-> new Http11NioProtocol(Http11NioProtocol)

public Http11NioProtocol() {
super(new NioEndpoint());
}

指定服务终端处理模型非阻塞nio

org.apache.tomcat.util.net.NioEndpoint

-> new NioEndPoint()

创建之后如何被启动?见springboot启动tomcat方式

终端处理线程和线程池初始化

启动之后

Http11NioProtocol执行bind()方法,

一些初始化,绑定端口

@Override
public void bind() throws Exception {
initServerSocket();
setStopLatch(new CountDownLatch(1));
// Initialize SSL if needed
initialiseSsl();
selectorPool.open(getName());
} //socket相关 initServerSocket()具体如下
// Separated out to make it easier for folks that extend NioEndpoint to
// implement custom [server]sockets
protected void initServerSocket() throws Exception {
//.......
//根据平台不同,反回具体底层类对象(windows,linux,unix)
serverSock = ServerSocketChannel.open();
socketProperties.setProperties(serverSock.socket());
//绑定地址和端口号
InetSocketAddress addr = new InetSocketAddress(getAddress(), getPortWithOffset());
serverSock.socket().bind(addr,getAcceptCount());
//.......
}

NioEndpoint初始化之后,调用start()执行startInternal()

代码如下

// Create worker collection
if (getExecutor() == null) {
//创建线程池
createExecutor();
} initializeConnectionLatch();
// Start poller thread
// 创建客户端队列(客户端过来的请求) poller = new Poller();
Thread pollerThread = new Thread(poller, getName() + "-ClientPoller");
pollerThread.setPriority(threadPriority);
pollerThread.setDaemon(true);
pollerThread.start(); //创建接收远程请求线程
startAcceptorThread();
初始化线程池配置

-> createExecutor() 用于处理用户请求

指定 备用线程,对大线程数,队列类型,超时时间,和线程工厂

public void createExecutor() {
internalExecutor = true;
TaskQueue taskqueue = new TaskQueue();
TaskThreadFactory tf = new TaskThreadFactory(getName() + "-exec-", daemon, getThreadPriority());
executor = new ThreadPoolExecutor(getMinSpareThreads(), getMaxThreads(), 60, TimeUnit.SECONDS,taskqueue, tf);
taskqueue.setParent( (ThreadPoolExecutor) executor);
}
创建Poller线程
poller = new Poller();
Thread pollerThread = new Thread(poller, getName() + "-ClientPoller");
pollerThread.setPriority(threadPriority);
pollerThread.setDaemon(true);
pollerThread.start();
创建Acceptor线程
protected void startAcceptorThread() {
acceptor = new Acceptor<>(this);
String threadName = getName() + "-Acceptor";
acceptor.setThreadName(threadName);
Thread t = new Thread(acceptor, threadName);
t.setPriority(getAcceptorThreadPriority());
t.setDaemon(getDaemon());
t.start();
}

处理请求的相关对象(线程)

Acceptor

org.apache.tomcat.util.net.Acceptor

Acceptor 负责循环等待远程请求,将请求以socket形式携带信息,调用setSocketOptions()将socket包装配置为socketWrapper,

setSocketOptions: 对socket包装处理配置,使用poller对象注册到队列,让poller线程做后续的处理

Acceptor 类的run方法:

public void run() {

        int errorDelay = 0;
//......以下省略部分代码
try {
// Loop until we receive a shutdown command
// 一直循环等待远程请求
while (!stopCalled) {
// Accept the next incoming connection from the server socket
// 1 接收请求
socket = endpoint.serverSocketAccept(); // setSocketOptions() will hand the socket off to
// 2 处理请求,setSocketOptions() 内部调用poller 将新请求任务放入队列
if (!endpoint.setSocketOptions(socket)) {
endpoint.closeSocket(socket);
} }
} finally {
stopLatch.countDown();
}
state = AcceptorState.ENDED;
}
Poller

org.apache.tomcat.util.net.NioEndpoint.Poller

Poller负责接收包装后的socket请求,放入队列,

并在run方法中循环去poll()请求任务,将与流读写有关的组件IOChannel Selector socketWrapper 绑定关联

再通过selector获取selectionKeys

迭代循环获取对应的socket,提交任务(线程),线程读写处理socketWrapper等后续操作

public void run() {
// Loop until destroy() is called
while (true) {
// poller队列任务处理 将IOChannel Selector socketWrapper 关联
hasEvents = events();
//......省略 Iterator<SelectionKey> iterator =
keyCount > 0 ? selector.selectedKeys().iterator() : null;
// Walk through the collection of ready keys and dispatch
// 非阻塞io api 任务处理
while (iterator != null && iterator.hasNext()) {
SelectionKey sk = iterator.next();
iterator.remove();
NioSocketWrapper socketWrapper = (NioSocketWrapper) sk.attachment();
// Attachment may be null if another thread has called
// cancelledKey()
if (socketWrapper != null) {
// 如果有等待处理的任务,则处理
processKey(sk, socketWrapper);
//processKey内部会调用processSocket方法,最终用线程池提交任务
}
} // Process timeouts
timeout(keyCount,hasEvents);
} getStopLatch().countDown();
}
其他

events队列

private final SynchronizedQueue<PollerEvent> events =
new SynchronizedQueue<>(); //事件队列(socket请求)
//注册请求到队列
public void rigister(final NioSocketWrapper socketWrapper)
{
event = new PollerEvent(socketWrapper, OP_REGISTER);
addEvent(event);
} private void addEvent(PollerEvent event) {
events.offer(event);
if (wakeupCounter.incrementAndGet() == 0) {
selector.wakeup();
}
}

events()绑定及后面的 processSocket()最终提交实际处理任务到线程

/**
* Processes events in the event queue of the Poller.
*
* @return <code>true</code> if some events were processed,
* <code>false</code> if queue was empty
*/
public boolean events() {
boolean result = false; PollerEvent pe = null;
for (int i = 0, size = events.size(); i < size && (pe = events.poll()) != null; i++ ) {
result = true;
NioSocketWrapper socketWrapper = pe.getSocketWrapper();
SocketChannel sc = socketWrapper.getSocket().getIOChannel();
int interestOps = pe.getInterestOps();
if (sc == null) {
log.warn(sm.getString("endpoint.nio.nullSocketChannel"));
socketWrapper.close();
} else if (interestOps == OP_REGISTER) {
try {
//注册绑定
sc.register(getSelector(), SelectionKey.OP_READ, socketWrapper);
} catch (Exception x) {
log.error(sm.getString("endpoint.nio.registerFail"), x);
}
} else {
final SelectionKey key = sc.keyFor(getSelector());
if (key == null) {
// The key was cancelled (e.g. due to socket closure)
// and removed from the selector while it was being
// processed. Count down the connections at this point
// since it won't have been counted down when the socket
// closed.
socketWrapper.close();
} else {
final NioSocketWrapper attachment = (NioSocketWrapper) key.attachment();
if (attachment != null) {
// We are registering the key to start with, reset the fairness counter.
try {
int ops = key.interestOps() | interestOps;
attachment.interestOps(ops);
key.interestOps(ops);
} catch (CancelledKeyException ckx) {
cancelledKey(key, socketWrapper);
}
} else {
cancelledKey(key, socketWrapper);
}
}
}
if (running && !paused && eventCache != null) {
pe.reset();//清空任务socketWrapper
eventCache.push(pe);
}
} return result;
}

setSocketOptions 中的socket任务注册

protected boolean setSocketOptions(SocketChannel socket) {
NioSocketWrapper socketWrapper = null;
try {
// Allocate channel and wrapper
NioChannel channel = null;
if (nioChannels != null) {
channel = nioChannels.pop();
}
//...... 部分省略
NioSocketWrapper newWrapper = new NioSocketWrapper(channel, this); socketWrapper.setKeepAliveLeft(NioEndpoint.this.getMaxKeepAliveRequests());
poller.register(socketWrapper);
return true;
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
try {
log.error(sm.getString("endpoint.socketOptionsError"), t);
} catch (Throwable tt) {
ExceptionUtils.handleThrowable(tt);
}
if (socketWrapper == null) {
destroySocket(socket);
}
}

学习tomcat-如何建立连接,处理请求的更多相关文章

  1. JDBC学习笔记之建立连接

    1. 引言 在一个JDBC应用程序中,如果想建立和数据源的连接,那么可以使用以下两个类: DriverManager:通过数据源的URL,我们可以建立与指定的数据源的连接.如果使用 JDBC 4.0 ...

  2. Spring学习11-Spring使用proxool连接池 管理数据源

    Spring 一.Proxool连接池简介及其配置属性概述   Proxool是一种Java数据库连接池技术.是sourceforge下的一个开源项目,这个项目提供一个健壮.易用的连接池,最为关键的是 ...

  3. 为什么TCP建立连接协议是三次握手,而关闭连接却是四次握手呢?

    看到了一道面试题:"为什么TCP建立连接协议是三次握手,而关闭连接却是四次握手呢?为什么不能用两次握手进行连接?",想想最近也到金三银四了,所以就查阅了相关资料,整理出来了这篇文章 ...

  4. tcp建立连接为什么需要三次握手和四次挥手

    前言 众所周知tcp传输层协议在建立连接的时候需要三次才能建立起一个真正的可靠连接,可是为什么是三次呢,不可以是两次,四次等等呢,可以自己思考一番,带着疑问可以看下文. 三次握手 在<计算机网络 ...

  5. 通俗易懂地讲解TCP建立连接的三次握手和释放连接的四次挥手

    TCP建立连接时,为什么要进行三次挥手? 每一次TCP连接都需要三个阶段:连接建立.数据传送和连接释放.三次握手就发生在连接建立阶段. 在谢希仁著<计算机网络>第四版中讲三次握手的目的是为 ...

  6. TCP建立连接的三次握手过程

    TCP是因特网中的传输层协议,使用三次握手协议建立连接,下面是TCP建立连接的全过程. 上图画出了TCP建立连接的过程.假定主机A运行的是TCP客户程序,B运行的是TCP服务器程序.最初两端的TCP进 ...

  7. Eclipse与tomcat服务器建立关联

    首先,点击 打开preference,打开如下界面 点击ADD,进入如下界面,选择tomcat服务器的版本->点击next 进入如下界面,Name:服务器名字,directory:服务器目录 补 ...

  8. TCP建立连接的3次握手和关闭连接的4次挥手

    #.3次握手过程状态 第一次握手:建立连接时,客户端发送SYN包(syn=j)到服务器,并进入SYN_SEND状态,等待服务器确认: 第二次握手:服务器收到SYN包,必须确认客户的SYN(ack=j+ ...

  9. TCP的三次握手(建立连接)与 四次挥手(关闭连接)

    一.TCP报文格式 TCP/IP协议的详细信息参看<TCP/IP协议详解>三卷本.下面是TCP报文格式图: TCP报文格式上图中有几个字段需要重点介绍下: (1)序号:Seq序号,占32位 ...

随机推荐

  1. Win10访问Ubuntu的samba共享文件

    大致分为以下几个步骤: 一.开启samba服务器 二.配置共享目录和用户权限 三.开启samba客户端 四.访问共享目录 一:开启samba服务器 安装samba服务器:  sudo apt-get ...

  2. iOS 索引列 使用详解

    做苹果开发的朋友在地区列表可能会遇到在页面的右侧有一列类似与导航的索引列,这次有机会遇到了,细细研究了一下,原来没有想象中的高达上,只需要简单的几步就能做出自己的索引列.,关注我的博客的朋友可能会对这 ...

  3. 2017年第八届蓝桥杯【C++省赛B组】B、C、D、H 题解

    可能因为我使用暴力思维比较少,这场感觉难度不低. B. 等差素数列 #暴力 #枚举 题意 类似:\(7,37,67,97,127,157\) 这样完全由素数组成的等差数列,叫等差素数数列. 上边的数列 ...

  4. linux设置共享文件夹 - samba

    安装samba sudo apt-get install samba 配置 /etc/samba/smb.conf 的global模块添加security = user 最下加入 [share] pa ...

  5. 老猿学5G:融合计费场景的离线计费会话的Nchf_OfflineOnlyCharging_Update 更新操作过程

    ☞ ░ 前往老猿Python博文目录 ░ 一.Nchf_OfflineOnlyCharging_Update消息交互过程 Nchf_OfflineOnlyCharging_Update消息是是5G融合 ...

  6. urllib.request.urlopen(req).read().decode解析http报文报“utf-8 codec can not decode”错处理

    老猿前期执行如下代码时报"'utf-8' codec can't decode byte"错,代码及错误信息如下: >>> import urllib.reque ...

  7. Docker 简介-基本概念(一)

    1.前提知识 1.1 linux相关的基础知识 1.2 git相关的知识 2. Docker三要素 Docker主要包含3要素:镜像(image).容器(container).仓库(repositor ...

  8. Aap.Net中的Action和Func委托

    前言 最近在阅读某开源框架源码的时候,发现作者在其中运用了很多Action委托和Func委托,虽然我之前在项目中也有一些对委托的实操,但还是免不了长时间的不用,当初消化的一些委托基础都遗忘了...索性 ...

  9. 团队作业part4--项目冲刺

    七天敏捷冲刺汇总 1. Day1 Scrum 冲刺博客 2. Day2 Scrum 冲刺博客 3. Day3 Scrum 冲刺博客 4. Day4 Scrum 冲刺博客 5. Day5 Scrum 冲 ...

  10. 微信小程序云开发如何上手

    简要介绍 微信小程序云开发,是基于 Serverless 的一站式后端云服务,涵盖函数.数据库.存储.CDN等服务,免后端运维.基于云开发可以免鉴权调用微信所有开放能力. 前提准备 微信开发者工具 创 ...