学习tomcat-如何建立连接,处理请求
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-如何建立连接,处理请求的更多相关文章
- JDBC学习笔记之建立连接
1. 引言 在一个JDBC应用程序中,如果想建立和数据源的连接,那么可以使用以下两个类: DriverManager:通过数据源的URL,我们可以建立与指定的数据源的连接.如果使用 JDBC 4.0 ...
- Spring学习11-Spring使用proxool连接池 管理数据源
Spring 一.Proxool连接池简介及其配置属性概述 Proxool是一种Java数据库连接池技术.是sourceforge下的一个开源项目,这个项目提供一个健壮.易用的连接池,最为关键的是 ...
- 为什么TCP建立连接协议是三次握手,而关闭连接却是四次握手呢?
看到了一道面试题:"为什么TCP建立连接协议是三次握手,而关闭连接却是四次握手呢?为什么不能用两次握手进行连接?",想想最近也到金三银四了,所以就查阅了相关资料,整理出来了这篇文章 ...
- tcp建立连接为什么需要三次握手和四次挥手
前言 众所周知tcp传输层协议在建立连接的时候需要三次才能建立起一个真正的可靠连接,可是为什么是三次呢,不可以是两次,四次等等呢,可以自己思考一番,带着疑问可以看下文. 三次握手 在<计算机网络 ...
- 通俗易懂地讲解TCP建立连接的三次握手和释放连接的四次挥手
TCP建立连接时,为什么要进行三次挥手? 每一次TCP连接都需要三个阶段:连接建立.数据传送和连接释放.三次握手就发生在连接建立阶段. 在谢希仁著<计算机网络>第四版中讲三次握手的目的是为 ...
- TCP建立连接的三次握手过程
TCP是因特网中的传输层协议,使用三次握手协议建立连接,下面是TCP建立连接的全过程. 上图画出了TCP建立连接的过程.假定主机A运行的是TCP客户程序,B运行的是TCP服务器程序.最初两端的TCP进 ...
- Eclipse与tomcat服务器建立关联
首先,点击 打开preference,打开如下界面 点击ADD,进入如下界面,选择tomcat服务器的版本->点击next 进入如下界面,Name:服务器名字,directory:服务器目录 补 ...
- TCP建立连接的3次握手和关闭连接的4次挥手
#.3次握手过程状态 第一次握手:建立连接时,客户端发送SYN包(syn=j)到服务器,并进入SYN_SEND状态,等待服务器确认: 第二次握手:服务器收到SYN包,必须确认客户的SYN(ack=j+ ...
- TCP的三次握手(建立连接)与 四次挥手(关闭连接)
一.TCP报文格式 TCP/IP协议的详细信息参看<TCP/IP协议详解>三卷本.下面是TCP报文格式图: TCP报文格式上图中有几个字段需要重点介绍下: (1)序号:Seq序号,占32位 ...
随机推荐
- 和功能相近的虚拟机软件相比,CrossOver的产品优势有哪些?
很多用户其实并不喜欢虚拟机软件,他们只是想用回熟悉的Windows应用程序,因为苹果系统与许多软件并不兼容.无奈之下,他们只能安装虚拟机软件.可是虚拟机软件大多比较笨重并且也相对复杂一些,在后期维护上 ...
- JS获取当前日期及 js获取当前时间和一星期前的时间
var myDate = new Date(); new Date() 代表当前 年 月 日 时 分 秒: myDate.getYear(); //获取当前年份(2位),getY ...
- Java Bean拷贝工具Orika原理解析
最近面试被问及对象拷贝怎样才能高效,实际上问的就是Orika或者BeanCopier的原理.由于网上对Orika原理的解析并不太多-因此本文重点讲解一下Orika的原理.(Orika是基于JavaBe ...
- vulnhub: DC 3
通过nmap扫描,只开放了80端口,并且该web服务是基于Joomla搭建: root@kali:~# nmap -A 192.168.74.140 Starting Nmap 7.80 ( http ...
- E. Number of Simple Paths 题解(思维)
题目链接 题目大意 给你n个点(\(\sum n<=2e5\)),n条边,求有多少条路径 题目思路 要明白任意两点的路径只能是1条或者2条 先topo找环(双向边也是可以找的) 然后把环上的每个 ...
- 多态,向上转型,向下转型,final关键字
多态 概述 多态封装性,继承性之后,面向对象的第三大特性. 定义 多态:是指同一种行为,具有多个不同的表现形式. 生活中,比如跑的动作,猫,狗,大象跑起来的动作都是不一样的,再比如飞的动作 ...
- Pycharm永久激活方法
1.下载新版破解补丁 链接 https://pan.baidu.com/s/137-afPKYfkXbvroSv1hoYw 提取码: cm43 下载补丁文件jetbrains-agent.jar并将 ...
- JQuery 和 Bootstrap
https://jquery.com/ 1. JQuery 的基础语法 $(select).action() 2. 查找标签 基本选择器 class选择器: $(".className&q ...
- 自动化运维工具之Puppet模块
前文我们了解来puppet的变量.流程控制.正则表达式.类和模板的相关话题,回顾请参考https://www.cnblogs.com/qiuhom-1874/p/14079208.html:今天我们来 ...
- go语言数据类型值--整型和浮点型
一.整型 1.整型的分类: 有符号整型: int8.int16.int32.int64 对应的无符号整型: uint8.uint16.uint32.uint64 uint就是我们熟知的byte类型,i ...