看tomcat源码的一点理解

http连接的表现就是socket对象

客户端连接服务端时,先随机分配一个端口去连接服务器的80端口,然后服务器会另外随机分配一个端口与客户端通信,该端口由一个socket持有。在程序上的表现就是:当这个socket还存在,就意味着客户端和服务端还在建立连接;反之,即断开连接。

通常情况下,连接会被很快的处理完,然后断开。当客户端再次需要请求服务器资源数,再重新经历上面的过程进行连接。这时客户端和服务端的通信端口还是会重新随机分配,每次连接可能都不一样。客户端在服务端是靠session唯一标识的,和端口没有关系。

所以,我们常说的服务器能够有多大的并发量,实际上就是指服务端能够同时处理多少socket对象。

Tomcat bio和nio的区别

看tomcat源码的一点理解_第1张图片

  • bio:只有Acceptor和Worker
  • nio:在Acceptor和Worker之间多了一个队列,Acceptor接收到请求之后,看Worker有没有空,有空就直接处理,没空的话,就先放到队列里。

socket对象在服务端的处理过程:

  1. 一开始建立连接,连接信息会存放在ServerSocket连接请求的队列中(队列长度为acceptCount)
  2. 如果现在提交的任务数没有超过maxConnections,那么就ServerSocket.accept()返回socket对象,封装为任务提交到线程池;现在提交的任务数超过了maxConnections,则阻塞
  3. 任务提交到线程池后,分三种情况
    1. 线程数<=minSpareThreads:不管有没有空闲线程,每次来任务都新建线程
    2. minSpareThreads<线程数
    3. 线程数==maxThreads:把任务放入任务队列中排队
  4. 当任务被处理完后,或队列已满则销毁任务以及任务中的socket对象

Tomcat连接数相关参数

  • minProcessors (minSpareThreads):最小空闲连接线程数,线程数小于此数时,每次来任务都新建线程处理,默认值为25
  • maxProcessors (maxThreads):最大连接线程数,线程数小于此数时,每次来任务如果没有空余线程才新建线程处理,默认值为200
  • acceptCount:允许的最大并发连接数(瞬时连接),为ServerSocket连接请求的队列长度,默认值为100
  • maxConnections:允许的最大连接数,计数参数,可以提交给线程池的最大任务数,默认值为10000
  • enableLookups:是否反查域名,取值为:true或false。为了提高处理能力,应设置为false
  • connectionTimeout:网络连接超时,单位:毫秒。设置为0表示永不超时,这样设置有隐患的。通常可设置为30000毫秒

Tomcat连接数相关参数对应程序

  • minProcessors和maxProcessors:连接线程数,对应HttpProcessor线程池中的线程数
  • acceptCount:允许的最大并发连接数,对应ServerSocket(int port, int backlog)中的backlog(连接请求的队列长度)
  • maxConnections:允许的最大连接数,线程池有个计数器connectionLimitLatch = new LimitLatch(getMaxConnections());,任务进入线程池时会调用countUpOrAwaitConnection(),任务完成时会调用countDownConnection()。如果线程池中的任务数超过maxConnections时,countUpOrAwaitConnection()就会阻塞,这样就可以阻止任务继续提交

maxConnections在不同模式下的默认值

当Tomcat接收的连接数达到maxConnections时,Acceptor线程不会读取accept队列中的连接;这时accept队列中的线程会一直阻塞着,直到Tomcat接收的连接数小于maxConnections。如果设置为-1,则连接数不受限制。

默认值与连接器使用的协议有关:NIO的默认值是10000,APR/native的默认值是8192,而BIO的默认值为maxThreads。
在windows下,APR/native的maxConnections值会自动调整为设置值以下最大的1024的整数倍;如设置为2000,则最大值实际是1024。

 

Tomcat中线程池解析

1. 线程池

executor = new ThreadPoolExecutor(getMinSpareThreads(), getMaxThreads(), maxIdleTime, TimeUnit.MILLISECONDS,taskqueue, tf);

 

2. 任务队列

taskqueue = new TaskQueue(maxQueueSize); //maxQueueSize = Integer.MAX_VALUE;

 

3. 部分代码

    public boolean offer(Runnable o) {
        //we can't do any checks
        if (parent==null) return super.offer(o);
        //we are maxed out on threads, simply queue the object
        //如果线程数==maxThreads,把任务放入任务队列中排队
        if (parent.getPoolSize() == parent.getMaximumPoolSize()) return super.offer(o);
        //we have idle threads, just add it to the queue
        //如果有空闲线程,就使用空闲线程
        if (parent.getSubmittedCount()<(parent.getPoolSize())) return super.offer(o);
        //if we have less threads than maximum force creation of a new thread
        //如果没有空闲线程才新建线程,并且线程数还没有达到maxThreads就返回false,这会导致#############1
        if (parent.getPoolSize()


思考:由此可见,用异常来控制逻辑,尽管很巧妙,但并不是一个好的设计 (←_←#)

在线用户数、连接数、瞬时并发数、线程数的区别

在线用户数=连接数+静态用户数(已登录,但连接已断开,只是在浏览静态数据)(有session对象,没有socket对象)
连接数=已接受连接数+瞬时并发数(acceptCount:在连接队列里等待的socket对象数) //这个状态的客户端显示为浏览器显示“转圈”
已接受连接数=线程数(RUNNABLE状态)(正在处理)+任务队列中的任务数(已接受,待处理)

对网络端口的理解

实际上,电脑在网卡上的硬件网络端口只有一个。我们所说的1-65535号端口,并不是真的有这么多个硬件端口,硬件端口实际上只有一个,访问所有端口的数据包都发往这个硬件端口。硬件端口接收到数据包之后进行解析,然后通知监听对应端口的socket对象来取数据。

参考

[1] 并发量计算

1.业务并发用户数;2.最大并发访问数;3.系统用户数;4.同时在线用户数;

假设一个OA系统有1000用户,这是系统用户数;最高峰同时有500人在线,是“同时在线人数”,也称作“最大业务并发用户数”;500个同时使用系统用户中20%查看系统公告,不构成压力;20%填写表格(只在提交时才会请求,填写对服务器不构成压力);40%在发呆(什么都没做);20%用户不停从一个页面跳转另一个页面(只有这20%对服务器产生了压力)。

说明服务器实际压力,能承受的最大并发访问数,既取决于业务并发用户数,还取决于用户的业务场景,这些可以通过对服务器日志的分析得到。

一般只需要分析出典型业务(用户常用,最关注的业务操作)
给出一个估算业务并发用户数的公式(测试人员一般只关心业务并发用户数)
C=nL/T
C^=C+3×(C的平方根)
C是平均的业务并发用户数、n是login session的数量、L是login session的平均长度、T是指考察的时间段长度、C^是指业务并发用户数的峰值。

该公式的得出是假设用户的login session产生符合泊松分布而估算得到。
假设OA系统有1000用户,每天400个用户发访问,每个登录到退出平均时间2小时,在1天时间内用户只在8小时内使用该系统。
C=400×2/8=100
C^=100+3×(100的平方根)=100+3×10=130
另外,如果知道平均每个用户发出的请求数u,则系统吞吐量可以估算为u×C

请注意:精确估算,还要考虑用户业务操作存在一定的时间集中性(比如上班后1小时内是OA系统高峰期),采用公式计算仍然会存在偏差。针对例子OA系统可以把1小时设定为考察时间的粒度,将一天8小时划分为8个区间,这样可以解决业务操作存在集中性问题,更趋于精准,偏差更小。

[2] 系统吞度量要素

系统吞吐量几个重要参数:QPS(TPS)、并发数、响应时间

QPS(TPS):每秒钟request/事务 数量

并发数: 系统同时处理的request/事务数

响应时间: 一般取平均响应时间

(很多人经常会把并发数和TPS理解混淆)

理解了上面三个要素的意义之后,就能推算出它们之间的关系:

QPS(TPS)= 并发数/平均响应时间

一个系统吞吐量通常由QPS(TPS)、并发数两个因素决定,每套系统这两个值都有一个相对极限值,在应用场景访问压力下,只要某一项达到系统最高值,系统的吞吐量就上不去了,如果压力继续增大,系统的吞吐量反而会下降,原因是系统超负荷工作,上下文切换、内存等等其它消耗导致系统性能下降。 决定系统响应时间要素

我们做项目要排计划,可以多人同时并发做多项任务,也可以一个人或者多个人串行工作,始终会有一条关键路径,这条路径就是项目的工期。

系统一次调用的响应时间跟项目计划一样,也有一条关键路径,这个关键路径是就是系统影响时间;

关键路径是有CPU运算、IO、外部系统响应等等组成。

[3] tomcat高并发配置与优化

 

 

tomcat的acceptCount与maxConnections

https://segmentfault.com/a/1190000008064162

 

SYN攻击

在三次握手过程中,Server发送SYN-ACK之后,收到Client的ACK之前的TCP连接称为半连接(half-open connect),此时Server处于SYN_RCVD状态,当收到ACK后,Server转入ESTABLISHED状态。SYN攻击就是 Client在短时间内伪造大量不存在的IP地址,并向Server不断地发送SYN包,Server回复确认包,并等待Client的确认,由于源地址 是不存在的,因此,Server需要不断重发直至超时,这些伪造的SYN包将产时间占用未连接队列,导致正常的SYN请求因为队列满而被丢弃,从而引起网络堵塞甚至系统瘫痪。SYN攻击时一种典型的DDOS攻击,检测SYN攻击的方式非常简单,即当Server上有大量半连接状态且源IP地址是随机的,则可以断定遭到SYN攻击了,使用如下命令可以让之现行:

netstat -nap | grep SYN_RECV

 

maxConnections表示有多少个socket连接到tomcat上。NIO模式下默认是10000。而maxThreads则是woker线程并发处理请求的最大数。也就是虽然client的socket连接上了,但是可能都在tomcat的task queue里头,等待worker线程处理返回响应。

 

tomcat server在tcp的accept队列的大小设置的基础上,对请求连接多做了一层保护,也就是maxConnections的大小限制。

当client端的大量请求过来时,首先是OS层的tcp的accept队列帮忙挡住,accept队列满了的话,后续的连接无法进入accept队列,无法交由工作线程处理,client将得到read timeout或者connection reset的错误。

第二层保护就是,在acceptor线程里头进行缓冲,当连接的socket超过maxConnections的时候,则进行阻塞等待,控制acceptor转给worker线程连接的速度,稍微缓缓,等待worker线程处理响应client。

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章