前言
- 最近看了很多tcp/ip 连接以及 IO相关的文章,但是依旧对数据库连接池等的部分不是很清楚, 所以这里仅是简单描述一下tomcat对应的http连接池数量的情况,不考虑与数据库的连接池的情况.
Linux内核相关
- 周天时简单总结了下linux内核的两个参数.somaxconn以及tcp_max_sync_backlog.这两天又学习了解了下.发现这两个配置文件其实讲的是全连接以及半连接的队列, 但是真正开始进入数据传输时应该就已经不在这两个队列里面了. 如下图所示.
- 所以前面两个参数应该是负责处理连接队列的,整整进入tomcat与client的交互时应该就不需要使用者两个队列了.核心就会变成其他的参数.
- 这里我理解核心的参数应该是 1. 当前操作系统剩余内存能够支撑的tcp连接数, 2.nofile 当前进程能够打开的文件数减去程序已经加载的文件数. 比如我们springboot的项目如果有1000个jar包可能就会产生1000-2000个fd的连接数,加上日志,以及一些监听pid等文件nofile -2000 左右才是可以使用的tcp连接数
- 这里 我的理解不一定正确, 我认为一个与tomcat 进行的tcp连接至少要占用一个文件描述符, 但是不清楚一个tcp连接复用多个http连接会不会占用更多的fd.
Tomcat相关
server:
#tomcat配置
tomcat:
# 当所有线程都在使用时,建立连接的请求的等待队列长度,默认100
accept-count: 1000
# 线程池维持最小线程数,默认10
min-spare-threads: 4
# 允许最大连接数,默认10000,当达到临界值时,系统可能会基于accept-count继续接受连接
max-connections: 10000
# 最大线程数,默认200
max-threads: 1000
max-thread: 应该就是 jstack-l 里面出现的 http 开头的thread信息, 随着threads 的数量的增加, 能够创建的线程会更多, 但是线程的创建和切换都是有成本的, 虽然线程能够共享进程的内存以及部分寄存器,但是上下文还是需要每次切换时进行switch, 线程多了切换的次数会变多,并且管理成本也比较高, 不建议设置的特别多,除非操作系统的cores 数特别多.
min-spare-threads: 这个参数可以应付在冷启动之后突然爆发的大量请求,避免一开会无法批量创建threads 时造成影响非常缓慢,建议可以保留一些.
需要注意的一点是 这两个线程都是 jvm 内部的线程, 内个现场都需要有 java的stack的size. 理论上默认值是 1m 左右, 可以设置为 256k 或者是更小 但是不能低于 172KB 还是一个具体的多大的数值, 但是如果栈空间过小, 会出现stack over flow的错误,并且递归的深度可能会不够, 不建议将栈空间设置的过小,毕竟会导致栈帧数量下降.
需要注意的另外一点,除非tomcat的http线程 还需要有类似于1. redis的连接线程. 2.gc线程. 3.数据库连接线程. 但是数据库的连接池好像不是在jvm的堆区和栈区管理的, 这一块需要找专家同事学习沟通一下. 我还没看到那一块内容.
max-connections: 关于这个参数,可能需要专门说明一下, 因为linux有BIO NIO以及AIO等IO模型,一般的说法是,自从tomcat 7 之后已经不支持BIO模型了,BIO时 没一个线程只能为一个 connection服务, 会导致吞吐量下降的很厉害. NIO非阻塞的IO时, 因为在内核和网卡驱动这一块时是不需要thread等待的, 所以thread可以为其他进程服务, 所以这个时候thread可以为远远大于自己的connections 来提供服务, 但是需要注意的一点是,如果是高CPU 低IO的场景, connection 是thread 的比较高的倍数时就显得不太合适了, 只有IO占用较多资源和时间时可以将connection 数值设置的thread大很多, 默认值就是10000.
关于这个10000 我这里的理解时 如果没个connection的round time 都是10s 左右的话如果每个线程只需要有1s左右的线程计算时间,那么理论上1000个thread就可以提供服务了. 但是具体的算法需要参照源码,可能比我想象的要复杂很多.
accept-count: 这个参数就会到了咱们内核参数里面的 somaxconn 参数对应了,就算这个参数可以设置的很大, 比如1000 但是实际上 具体的连接池大小是收到somaxconn和accept-count里面的较小值来决定的, 所以如果线程数较少,并且并发数产生了很多的连接数时很容易就会将max-connections 的数值打满,然后开始用accept-count的参数值,如果somaxconn是默认值的话 比如centos7 上面都是 128 那么第 10129个连接应该直接就会被linux 给drop 客户端返回connection reset 的提示信息 (自己推断的,还没明确实验)
IO相关
- 最近有很多公众号在将linux的IO模型,最近一个月以来也看了很多内容,理解的不是很透彻, 这里简单总结一下.
IO有很多种类,磁盘是IO,键盘是IO,网络栈的收发其实也是IO操作.
因为CPU都是纳秒级别的反应, 网络的IO都是几十上百毫秒的延迟, 这里面有六七个数量级的延迟,为了不让CPU干耗着去等待缓慢的网络IO(机械磁盘IO也是如此),在最开始的blocked IO 基础之上 慢慢研发了NIO和AIO.
需要注意的是linux比较多的是NIO noblocked 但是AIO 也就是异步非阻塞IO实现的比较少一些.
阻塞和非阻塞IO的核心区别在于 在CPU获取到需要的IO数据之前 用户态的线程是在等待还是在干别的
如果在等待,那么就是阻塞IO,如果不等待而去干别的了 就是非阻塞IO.
CPU获取到数据之后,如果从内核态往用户态传输数据时是需要用户态线程参与的,就还是同步的
如果只是CPU将需要的IO数据复制到了用户态需要使用的地方,直接通知用户线程进行处理,那么就是异步的.
需要注意的是 linux还有一个zero copy的概念, 其实与内存里面的 copy on write 类似
都是避免不必要的内存读写,节约宝贵的总线带宽或者是内存带宽拉实现提高性能的操作.
nginx 就使用了sendfile的方式进行 zero copy的机制来提高静态文件的传输速度, 记得能够减少一次内核态到用户态的文件copy,只是传递fd就可以让网卡直接传输client想服务器申请访问的资源.
tomcat 作为 app server 感觉这一块不如 nginx这种io复用的纯web server 处理静态资源来的省时省力.
所以tomcat有一个handler 可以进行判断请求类型,如果是静态类型的话 交由一个类似的nginx web server 来处理,但是还是感觉会导致tomcat的负载变大, 建议还是前端静态文件还是专门有一个静态文件服务器来跑比较好一些.
疑惑的地方
- springboot 开起来的进程之后线程数其实非常多,有redis的,有操作连接池的,也有tomcat用来实现http连接等的. 还有跟当前CPU数目一致数量的GC线程. 但是我一直不知道数据库连接池是如何管理的,虽然能在jstack 打开的thread 信息里面看到hikari pool 等的相关信息.但是感觉这些只是springboot 进行使用数据库连接池的进程,而不是管理数据库连接池的线程.
- dump堆栈信息里面包含的内容,是否不包含native memory和method pool等. 以及连接池到底存放在何处,如何进行监控,如果单一个db多个app-server时 如果每一个app-server都保持一个比较多的数据量连接池,那么会不会对数据库服务器有较大的压力存在?