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. 为什么换了电脑安装MindManager提示密钥失效?

    相信很多MindManager用户遇到过这样的问题,不想在原电脑上使用MindManager思维导图软件,想要换电脑安装,但是提示该许可证密钥失效了.下面文章就教大家如何解决这个问题: 我们在Mind ...

  2. kakafka - 为CQRS而生fka - 为CQRS而生

    前段时间跟一个朋友聊起kafka,flint,spark这些是不是某种分布式运算框架.我自认为的分布式运算框架最基础条件是能够把多个集群节点当作一个完整的系统,然后程序好像是在同一台机器的内存里运行一 ...

  3. Contest 982

    A 直接模拟即可,为了方便边界判断建议用 !=. 时间复杂度 \(O\left(n\right)\). B \(w\) 排序来处理内向者,坐人后丢进大根堆来处理外向者. 时间复杂度 \(O\left( ...

  4. Matlab 数组

    数组创建 1:逐个元素输入法:如:x=[1 2 3 4 5](中间也可以用逗号隔开) 2:冒号法:如:x=1:1:5 %从1到5步长为1 3:linspace 法: ----创建线性等距的数组 lin ...

  5. C语言讲义——开发工具Dev C++

    20世纪60年代,编程语言界发生"结构化程序设计"变革, 丹尼斯·里奇(Dennis Ritchie)& 肯·汤普森(Ken Thompson)发明C语言,率先建立了面向过 ...

  6. 专业五线谱作曲打谱软件Overture的常用快捷键功能大全

    快捷命令在我们使用软件时起到的帮助是不言而喻的,它用一个或几个简单的字母来代替常用的命令,使我们不用去记忆众多的长长的命令,也不必为了执行一个命令,在菜单和工具栏上寻寻觅觅.当然,随着Overture ...

  7. 跟随杠精的视角一起来了解Redis的主从复制

    不想弹好吉他的撸铁狗,都不是好的程序猿 虽然说单机的Redis性能很好,也有完备的持久化机制,那如果你的业务体量真的很大,超过了单机能够承载的上限了怎么办?不做任何处理的话Redis挂了怎么办?带着这 ...

  8. day6(短信验证接口)

    1.注册容联云账号 1.1注册账号 https://www.yuntongxun.com/user/login  1.2登录即可看到开发者账号信息 1.3 添加测试账号 2.使用容联云发送代码测试 ' ...

  9. 在django中使用原生sql语句

    raw # row方法:(掺杂着原生sql和orm来执行的操作) res = CookBook.objects.raw('select id as nid from epos_cookbook whe ...

  10. Python中import模块时报SyntaxError: (unicode error)utf-8 codec can not decode 错误的解决办法

    老猿有个通过UE编辑(其他文本编辑器一样有类似问题)的bmi.py文件,在Python Idle环境打开文件执行时没有问题,但import时报错: SyntaxError: (unicode erro ...