赞
踩
我们知道在Linux中一切皆文件,那么一台服务器最大能打开多少个文件呢?Linux上能打开的最大文件数量受三个参数影响,分别是:
fs.file-max
(系统级别参数):该参数描述了整个系统可以打开的最大文件数量。但是root用户不会受该参数限制(比如:现在整个系统打开的文件描述符数量已达到fs.file-max
,此时root用户仍然可以使用ps、kill等命令或打开其他文件描述符)soft nofile
(进程级别参数):限制单个进程上可以打开的最大文件数。只能在Linux上配置一次,不能针对不同用户配置不同的值fs.nr_open
(进程级别参数):限制单个进程上可以打开的最大文件数。可以针对不同用户配置不同的值这三个参数之间还有耦合关系,所以配置值的时候还需要注意以下三点:
soft nofile
,那么hard nofile
参数值也需要一起调整。如果因为hard nofile
参数值设置的低,那么soft nofile
参数的值设置的再高也没有用,实际生效的值会按照二者最低的来。hard nofile
,那么fs.nr_open
也都需要跟着一起调整(fs.nr_open
参数值一定要大于hard nofile
参数值)。如果不小心把hard nofile
的值设置的比fs.nr_open
还大,那么后果比较严重。会导致该用户无法登录,如果设置的是*,那么所有用户都无法登录fs.nr_open
,但是是用的echo "xxx" > ../fs/nr_open
命令来修改的fs.nr_open
的值,那么刚改完可能不会有问题,但是只要机器一重启,那么之前通过echo命令设置的fs.nr_open
值便会失效,用户还是无法登录。所以非常不建议使用echo的方式修改内核参数!!!假设想让进程可以打开100万个文件描述符,这里用修改conf文件的方式给出一个建议。如果日后工作里有类似的需求可以作为参考。
vim /etc/sysctl.conf
fs.file-max=1100000 // 系统级别设置成110万,多留点buffer
fs.nr_open=1100000 // 进程级别也设置成110万,因为要保证比 hard nofile大
使上面的配置生效sysctl -p
vim /etc/security/limits.conf
// 用户进程级别都设置成100完
soft nofile 1000000
hard nofile 1000000
我们知道TCP连接,从根本上看其实就是client和server端在内存中维护的一组【socket内核对象】(这里也对应着TCP四元组:源IP、源端口、目标IP、目标端口),他们只要能够找到对方,那么就算是一条连接。那么一台服务器最大能建立多少条连接呢?
如果只以ESTABLISH
状态的连接来算(这些连接只是建立,但是不收发数据也不处理相关的业务逻辑)那么一台服务器最大能建立多少连接呢?以一台4GB内存的服务器为例!
ESTABLISH
状态的空闲连接,不会消耗CPU(虽然有TCP保活包传输,但这个影响非常小,可以忽略不计)ESTABLISH
状态的连接大约消耗【3.3KB内存】,那么通过计算得知一台4GB内存的服务器,【可以建立100w+的TCP连接】(当然这里只是计算所有的连接都只建立连接但不发送和处理数据的情况,如果真实场景中有数据往来和处理(数据接收和发送都需要申请内存,数据处理便需要CPU),那便会消耗更高的内存以及占用更多的CPU,并发不可能达到100w+)上面讨论的都是进建立连接的理想情况,在现实中如果有频繁的数据收发和处理(比如:压缩、加密等),那么一台服务器能支撑1000连接都算好的了,所以一台服务器能支撑多少连接还要结合具体的场景去分析,不能光靠理论值去算。抛开业务逻辑单纯的谈并发没有太大的实际意义。
服务器的开销大头往往并不是连接本身,而是每条连接上的数据收发,以及请求业务逻辑处理!!!
我们知道客户端每和服务端建立一个连接便会消耗掉client端一个端口。一台机器的端口范围是【0 ~ 65535】,那么是不是说一台client机器最多和一台服务端机器建立65535个连接呢(这65535个端口里还有很多保留端口,可用端口可能只有64000个左右)?
由TCP连接的四元组特性可知,只要四元组里某一个元素不同,那么就认为这是不同的TCP连接。所以需要分情况讨论:
65535
个连接n * 65535
个
n * 65535
个连接了65535 * m
65535 * m
个net.ipv4.ip_local_port_range
限制,如果要修改client所能使用的端口范围,可以修改这个内核参数的值。三次握手里socket的全连接队列长度由参数net.core.somaxconn
来控制,默认大小是128,当两台机器离的非常近,但是建立连接的并发又非常高时,可能会导致半连接队列或全连接队列溢出,进而导致server端丢弃握手包。然后造成client超时重传握手包(至少1s以后才会重传),导致三次握手连接建立耗时过长。我们可以调整参数net.core.somaxconn
来增加去按连接队列的长度,进而减小丢包的影响
有时候我们通过 ctrl + c
方式来终止了某个进程,但是当重启该进程的时候发现报错端口被占用
,这种问题是因为【操作系统还没有来得及回收该端口,等一会儿重启应用就好了】
client程序在和server端建立连接时,如果client没有调用bind方法传入指定的端口,那么client在和server端建立连接的时候便会自己随机选择一个端口来建立连接。一旦我们client程序调用了bind方法传入了指定的端口,那么client将会使用我们bind里指定的端口来和server建立连接。所以不建议client调用bind方法,bind函数会改变内核选择端口的策略
public static void main(String[] args) throws IOException {
SocketChannel sc = SocketChannel.open();
// 客户端还可以调用bind方法
sc.bind(new InetSocketAddress("localhost", 9999));
sc.connect(new InetSocketAddress("localhost", 8080));
System.out.println("waiting..........");
}
在Linux一切皆文件,当然也包括之前TCP连接中说的socket。进程打开一个socket的时候需要创建好几个内核对象,换一句直白的话说就是打开文件对象吃内存
,所以Linux系统基于安全角度考虑(比如:有用户进程恶意的打开无数的文件描述符,那不得把系统搞奔溃了),在多个位置都限制了可打开的文件描述符的数量
。
内核是通过【hash表
】的方式来管理所有已经建立好连接的socket,以便于有请求到达时快速的通过【TCP四元组
】查找到内核中对应的socket对象
在网络开发中,很多人对一个基础问题始终没有彻底搞明白,那就是一台机器最多能支撑多少条TCP连接。不过由于客户端和服务端对端口使用方式不同,这个问题拆开来理解要容易一些。
注意,这里说的是客户端和服务端都只是角色,并不是指某一台具体的机器。例如对于我们自己开发的应用程序来说,当他响应客户端请求的时候,他就是服务端。当他向MySQL请求数据的时候,他又变成了客户端。
你在线上可能遇到过too many open files
这个错误,那么你理解这个报错发生的原理吗?如果让你修复这个错误,应该如何处理呢?
fs.file-max
、soft nofile
、fs.nr_open
这三个参数的值来修改进程能打开的最大文件描述符数量
因为这里要考虑的是最大数,因此先不考虑连接上的数据收发和处理,仅考虑ESTABLISH
状态的空连接。那么一台服务端机器上最大可以支持多少条TCP连接?这个连接数会受哪些因素的影响?
ESTABLISH状态
下的空连接情况下,一台服务器上最大可支持的TCP连接数量基本上可以说是由内存大小来决定的。文件句柄
】而添加的限制。这个限制只要修改几个内核参数就可以加大。和服务端不同的是,客户端每次建立一条连接都需要消耗一个端口。在TCP协议中,端口是一个2字节的整数,因此范围只能是0~65535。那么客户单最大只能支持65535条连接吗?有没有办法突破这个限制,有的话有哪些办法?
假设你是系统架构师,现在老板给你一个需求,让你做一个类似友盟upush这样的产品。要在服务端机器上保持一个和客户端的长连接,绝大部分情况下连接都是空闲的,每天也就顶多推送两三次左右。总用户规模预计是1亿。那么现在请你来评估一下需要多少台服务器可以支撑这1亿条长连接。
参考:《深入理解Linux网络》书籍
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。