当前位置 博文首页 > 努力充实,远方可期:【tomcat】3、预备知识与参数解析
socket是tcp层上面封装的协议,http是应用层封装的协议
计算机网络的知识告诉我们,
流程:
要想明白Socket连接,先要明白TCP连接。手机能够使用联网功能是因为手机底层实现了TCP/IP协议,可以使手机终端通过无线网络建立TCP连接。TCP协议可以对上层网络提供接口,使上层网络数据的传输建立在“无差别”的网络之上。
建立起一个TCP连接需要经过“三次握手”:
握手过程中传送的包里不包含数据,三次握手完毕后,客户端与服务器才正式开始传送数据。理想状态下,TCP连接一旦建立,在通信双方中的任何一方主动关闭连接之前,TCP 连接都将被一直保持下去。断开连接时服务器和客户端均可以主动发起断开TCP连接的请求,断开过程需要经过“四次握手”(过程就不细写了,就是服务器和客户端交互,最终确定断开)
HTTP协议即超文本传送协议(Hypertext Transfer Protocol ),是Web联网的基础,也是手机联网常用的协议之一,HTTP协议是建立在TCP协议之上的一种应用。作为应用层协议,HTTP是基于TCP/IP协议来传递数据的(HTML文件、图片、查询结果等),HTTP协议不涉及数据包(Packet)传输,主要规定了客户端和服务器之间的通信格式。
HTTP连接最显著的特点是客户端发送的每次请求都需要服务器回送响应,在请求结束后,会主动释放连接。从建立连接到关闭连接的过程称为“一次连接”。
HTTP 1.0
中,客户端的每次请求都要求建立一次单独的连接,在处理完本次请求后,就自动释放连接。也就是不支持长连接HTTP 1.1
中,可以在一次连接中处理多个请求,并且多个请求可以重叠进行,不需要等待一个请求结束后再发送下一个请求。尽管HTTP1.1开始支持持久连接,但仍无法保证始终连接。而Socket连接一旦建立TCP三次握手,除非一方主动断开,否则连接状态一直保持。由于HTTP在每次请求结束后都会主动释放连接,因此HTTP连接是一种“短连接”,要保持客户端程序的在线状态,需要不断地向服务器发起连接请求。通常的做法是即时不需要获得任何数据,客户端也保持每隔一段固定的时间向服务器发送一次“保持连接”的请求,服务器在收到该请求后对客户端进行回复,表明知道客户端“在线”。若服务器长时间无法收到客户端的请求,则认为客户端“下线”,若客户端长时间无法收到服务器的回复,则认为网络已经断开。
一个socket里发送了多个http请求,每个http请求会判断请求头里的connection是否close,是的话处理完关闭socket。
如果是connection:keep-alive,则不会断开。另外tomcat里也有限制同时能同时存在几个活跃的socket,还有一个socket可以处理几个http请求。
对方revcbuff满了就阻塞了
连接
是TCP
层面的(传输层),对应socket;请求
是HTTP
层面的(应用层),必须依赖于TCP的连接实现;Socket是对TCP/IP协议的封装,Socket本身并不是协议,而是一个调用接口(API),通过Socket,我们才能使用TCP/IP协议。
一个socket的步骤:
在Netty等文章中说了太多回了,我懒得说了,复制点内容吧
socket是”打开—读/写—关闭”模式的实现,以使用TCP协议通讯的socket为例,其交互流程大概是这样子的
看不懂这没关系,我这里是为了解释,只是为了给源码铺垫,后面我会再指引回来
对我而言,我需要注意bind()那个函数,原来在学习中老师都是交绑定一个本地的接口就可以了,但在tomcat源码阅读中,出现了acceptor这个东西,而且还是个数组,据我观察,他就是bind()里的backlog
,怎么理解呢?
在查阅手册里,有如下的内容
public void bind(SocketAddress endpoint,//绑定到的IP地址和端口号。
int backlog)//请求进入连接队列的最大长度。
将ServerSocket绑定到特定地址(IP地址和端口号)。 如果地址为null ,则系统将接收临时端口和有效的本地地址来绑定套接字。
backlog参数是套接字上请求的最大挂起连接数。 其确切语义是实现具体的。 特别地,实现可以施加最大长度,或者可以选择忽略参数altogther。 提供的价值应大于0 。 如果小于或等于0 ,则将使用实现特定的默认值。
再结合tomcat里acceptor的知识,那么我就总结如下,
从我这个想法出发,也就解释了tomcat的最大连接数为什么是 maxConnections+maxAcceptors 两者和的值。
另外tcp握手和这个accept的逻辑前后关系?网上说三次握手后才进入accept队列,但我更认为accept就是3次握手,具体源码应该在linux中看到,先不验证,没大所谓
https://blog.csdn.net/mccand1234/article/details/91346202
长连接指的是建立了tcp之后可以多次发送http。
长连接:指在一个TCP连接上可以连续发送多个数据包,在TCP连接保持期间,如果没有数据包发送,需要双方发检测包以维持此连接(心跳包),一般需要自己做在线维持。
短连接:是指通信双方有数据交互时,就建立一个TCP连接,数据发送完成后,则断开此TCP连接。
比如Http的,只是连接、请求、关闭,过程时间较短,服务器若是一段时间内没有收到请求即可关闭连接。其实长连接是相对于通常的短连接而说的,也就是长时间保持客户端与服务端的连接状态。
什么时候用长连接,短连接:
HTTP连接与Socket连接的区别:
servlet三大组件:servlet、filter、listener。
chain.doFilter(request, response);
调用servlet,可以实现在前后打印日志我们定义类实现HttpServlet,放到tomcat中,即可拦截到请求。对应的req和resp实例是tomcat创建的
public class MyServlet extends HttpServlet{
// 重写doGet方法
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp){
resp.getWriter().println("hhh");
}
}
如下图,浏览器发HTTP请求到服务器后,
这里的服务器指的是后文的连接器,也就是说服务器应该只负责连接,连接后的结果应该交给servlet容器处理
如果要把业务类放到servlet容器中,那么它首先得是servlet,那么就设计了servlet接口。接口+容器=servlet规范
而tomcat就是实现了servlet规范,他可以放servlet,他就是一个servlet容器。要实现逻辑,只需要实现servlet然后注册到tomcat等servlet容器即可。
我们的jsp是由dispatchServlet派发过来的,原理还是servlet
客户发起请求,http服务器利用连接器把请求获取到,然后把请求交给servlet容器。
连接器把请求先封装为request对象,然后再由适配器封装为servlet request对象,然后就交给servlet容器,就可以调用servlet.service()方法了(会先调用servlet.init()
)
servlet的知识告诉我们实现service()、doGet()、doPost()等方法都是一个道理。你实现了service()方法就不用父类的service()方法了,你没实现service()方法的话就调用父类的service()方法,父类的service()会调用子类的doGet()、doPost()
另外servlet正常是第一次被访问才创建对象,而在spring中我们是容器初始化后就创建了servlet对象调用了init方法,然后直接可以调用service()
BIO是阻塞的,NIO是非阻塞的,而且可以利用事件机制。
请参考本人博客中其他NIO的知识吧,这里不具体说了,主要继承在netty中。本文只会把tomcat的组件对应到nio的组件上
在使用tomcat时,经常会遇到连接数、线程数之类的配置问题,我们先从NIO方面了解一下,后面再从tomcat的角度了解一下
BIO的知识告诉我们,因为accept()、read()、write()都是阻塞的方法,造成即使新的连接来了,还是得等当前连接处理完再处理其他连接。然后BIO升级为了线程池版,也就是说read()等业务方法很耗时,那么就可以accept()后把read()逻辑放到线程池里去执行。他的弊端是:
NIO的知识告诉我们,连接可以注册到selector上,然后利用事件机制selector.select()
获取到哪些连接发送事件需要处理了。这就解决了一个线程可以处理多个连接的问题。
与NIO更好的是APR(Apache Portable Runtime),是Apache可移植运行库,利用本地库可以实现高可扩展性、高性能;Apr是在Tomcat上运行高并发应用的首选模式,但是需要安装apr、apr-utils、tomcat-native等包。
个人不熟悉,先不讨论
再多嘴一句,BIO NIO是TCP层之上的协议,不是HTTP协议,他是对socket的封装
tomcat因为可以注册多个连接器,但是每个连接器都是一种协议protocol。
暂时你先把连接器认为是 接收请求tcp三次握手+形成request 的工具即可。
其中最典型的protocol包括BIO、NIO和APR(Tomcat7中支持这3种,Tomcat8增加了对NIO2的支持,而到了Tomcat8.5和Tomcat9.0,则去掉了对BIO的支持)。
连接器Connector使用哪种protocol,可以通过<connector>
元素中的protocol属性进行指定,也可以使用默认值。
对于每个连接器标签<connector>
,可以指定是用于处理HTTP
的还是AJP
请求的,可以处理不同的协议。而HTTP又分为是用哪种通信方式,阻塞还是非阻塞的
BIO
(Tomcat7默认)NIO
(Tomcat8默认)NIO2
APR
(Tomcat7、8默认,前提是能找到需要的本地库)上面NIO的优势在于用几个线程管理很多连接,或者说一个线程selector去进行selector.select()
就可以管理至少1024个连接
线程和CPU的关系:
我们知道java可以new很多线程,线程和cpu什么关系?
线程数可以远远大于CPU个数。
比如线程要去进行(等待数据库返回、读写硬盘、读网卡)或者调用了object.wait()等操作,那么就阻塞了。读网卡什么的操作是可以用DMA去准备数据的,DMA准备好数据后,像CPU发起硬件中断,CPU才继续执行该线程,在这期间CPU可以取处理被的线程。所以说,CPU可以处理很多线程
在一、2介绍过bind()于accept的关系
accept表示连接等待队列的长度,如果达到该值,请求都不会等待,用户那直接报错connection refused
了
当到达最大连接数后,tomcat后将后面的请求存放到任务队列进行排序,任务对垒中排队等待的个数达到acceptCount时,拒绝。
一台tomcat的最大请求数量是 maxConnections+acceptCount
当accept队列中连接的个数达到acceptCount时,队列满,进来的请求一律被拒绝。默认值是100
。
acceptCount的设置,与应用在连接过高情况下希望做出什么反应有关系。如果设置过大,后面进入的请求等待时间会很长;如果设置过小,后面进入的请求立马返回connection refused。
Tomcat在任意时刻接收和处理的最大连接数。当Tomcat接收的连接数达到maxConnections时,Acceptor线程
不会读取accept队列中的连接;这时accept队列中的线程会一直阻塞着,直到Tomcat接收的连接数小于maxConnections。如果设置为-1,则连接数不受限制。
默认值与连接器使用的协议有关:NIO
的默认值是10000,APR/native
的默认值是8192,而BIO
的默认值为maxThreads(如果配置了Executor,则默认值是Executor的maxThreads)。
在windows下,APR/native的maxConnections值会自动调整为设置值以下最大的1024的整数倍;如设置为2000,则最大值实际是1024。
可以使用ulimit -a
查看服务器限制,对于CPU要求更高(计算型)时,不建议配置过大。对于CPU要求不是特别高时,配置建议在2000
左右。
另外,BIO的知识告诉我们,最大线程数==最大连接数,因为没有注册事件机制,必须自己去轮询处理。
NIO的最大线程数<<最大连接数
还有,服务器中可以同时接收的连接数为maxConnections+acceptCount
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
<Connector protocol="AJP/1.3"
address="::1"
port="8009"
redirectPort="8443" />
假设Tomcat接收http请求的端口是8083
,则可以使用如下语句查看连接情况:
# 查看服务器访问数量
netstat -an | grep ESTABLISHED | wc -l
# 查看端口的连接数
netstat -nat|grep -i "80"|wc -l
netstat –nat | grep 8083
结果如下所示:
可以看出,
listen
状态,监听请求;ESTABLISHED
)CLOSE_WAIT
)。# 查看进程数
ps aux|grep httpd|wc -l
ps -ef|grep httpd|wc -l
# 查看Apache的并发请求数及其TCP连接状态:
netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'
返回结果示例:
LAST_ACK 5
SYN_RECV 30
ESTABLISHED 1597
FIN_WAIT1 51
FIN_WAIT2 504
TIME_WAIT 1057
其中的
SYN_RECV表示正在等待处理的请求数;
ESTABLISHED表示正常数据传输状态;
TIME_WAIT表示处理完毕,等待超时结束的请求数。
这里指的是处理请求的线程数,前提是已经通过accept()了,是指去线程池里进行read的操作的线程
请求处理线程的最大数量。默认值是200(Tomcat7和8)。如果该Connector绑定了Executor,这个值会被忽略,因为该Connector将使用绑定的Executor,而不是内置的线程池来执行任务。
ps命令可以查看进程状态,如执行如下命令:
ps –e | grep java
结果如下图:
可以看到,只打印了一个进程的信息;27989是进程id,java是指执行的java命令。这是因为启动一个tomcat,内部所有的工作都在这一个进程里完成,包括主线程、垃圾回收线程、Acceptor线程、请求处理线程等等。
通过如下命令,可以看到该进程内有多少个线程;其中,nlwp
含义是number of light-weight process
ps –o nlwp 27989
# 输出
NLWP
73
可以看到,该进程内部有73个线程;但是73并没有排除处于idle状态的线程。要想获得真正在running
的线程数量,可以通过以下语句完成:
ps -eLo pid ,stat | grep 27989 | grep running | wc -l
其中ps -eLo pid ,stat可以找出所有线程,并打印其所在的进程号和线程当前的状态;两个grep命令分别筛选进程号和线程状态;wc统计个数。其中,ps -eLo pid ,stat | grep 27989输出的结果如下:
图中只截图了部分结果;Sl表示大多数线程都处于空闲状态。
这个部分的内容可以跳过
KeepAlive
:KeepAlive是在HTTP1.1中定义的,用来保持客户机和服务器的长连接,通过减少建立TCP Session的次数来提高性能。常用的配置参数有{KeepAlive, KeepAliveTimeout, MaxKeepAliveRequests
}。KeepAliveTimeout
:决定一个KeepAlive的连接能保持多少时间,Timeout就尽快shutdown链接,若还有数据必须再建立新的连接了;其实是read()
阻塞时间。长连接就是读完最后一次请求的20s后关闭socketMaxKeepAliveRequests
:服务多少个请求就shutdown关闭socket连接。maxThreads
:tomcat起动的最大线程数,即同时处理的任务个数,默认值为200
maxConnections
:tomcat能够接受的最大连接数,maxConnections 默认值是10000。但是还有个参数acceptCount可以决定连接数的大小
acceptCount
:当tomcat起动的线程数达到最大时,接受排队的请求个数,默认值为100
https://blog.csdn.net/quliuwuyiz/article/details/79979031
用一个形象的比喻,通俗易懂的解释一下tomcat的最大线程数(maxThreads)、最大等待数(acceptCount)和最大连接数(maxConnections)三者之间的关系。
我们可以把tomcat比做一个火锅店,流程是取号、入座、叫服务员,可以做一下三个形象的类比:
(1)acceptCount 最大等待数
可以类比为火锅店的排号处能够容纳排号的最大数量;排号的数量不是无限制的,火锅店的排号到了一定数据量之后,服务往往会说:已经客满。
(2)maxConnections 最大连接数
可以类比为火锅店的大堂的餐桌数量,也就是可以就餐的桌数。如果所有的桌子都已经坐满,则表示餐厅已满,已经达到了服务的数量上线,不能再有顾客进入餐厅了。
(3)maxThreads:最大线程数
可以类比为厨师的个数。每一个厨师,在同一时刻,只能给一张餐桌炒菜,就像极了JVM中的一条线程。
maxConnections、maxThreads、acceptCount关系图如下