跳到主要内容

转发服务的设置和Linux系统参数调优

· 阅读需 12 分钟
KKY

概要

转发服务的一个需要解决的重要问题是,解决端口耗尽的问题。在没有设置的情况下,程序只能发起不足3万的连接。 加上接入端的断开重连、假连接等情况,实际有效的连接数就更少。而解决这个问题, 目前主流的解决方法是增加IP和可用的端口范围,并自已管理端口。

围绕这个目标并实现一些简单的优化,可对转发服务和Linux系统参数进行以下设置、调优:

  1. 调整最大打开文件数的限制
  2. 做好IP和端口规划
  3. 调整部分TCP参数

本文虽然针对转发服务和Linux操作系统,但这里解决问题的基本思路也是适用于其他需要大量外发连接的应用和其他操作系统的。

注:大量外发时,路由器防火墙等设备也要作出相应的调整,如增加IP,缩短数据包超时等。具体请参考这些网络设备的说明。

调整最大打开文件数的限制

在Linux中,为了防止某些应用或用户占用过多的系统资源,系统限定用户最大可同时打开的文件句柄数量。 除了读写普通文件以外,高负载的转发服务会使用较多的TCP连接,而每个TCP连接对应被系统视为一个文件, 都要受到这个最大可同时打开文件数的限制。所以,要确保运行转发服务的用户的这个限制值高于连接需求。

查看最大打开文件数

ulimit -n

临时修改最大打开文件数

如果查到的最大打开文件数小于期望的值,可通过下面的命令临时修改:

ulimit -n {limit_value}

其中,{limit_value}为期望的最大打开文件数,如:

ulimit -n 1000000

以上将最大打开文件数改为100万,实际大部分应用的需求远远少于这个值,如,转发在10万连接以下的,可以改为10万。 下同,示例中的100万可改为实际需要的值。

永久修改最大打开文件数

修改 /etc/security/limits.conf 文件,添加或修改以下行:

{user} hard nofile {limit_value}
{user} soft nofile {limit_value}

其中,{user}为执行转发服务的用户的用户名或用户组(group),{limit_value}为期望的最大打开文件数,如:

relay hard nofile 1000000
relay soft nofile 1000000

* 号可以设置所有的用户,如:

* hard nofile 1000000
* soft nofile 1000000

修改 limits.conf 文件后需要重启服务器方才生效,可用ulimit -n命令先应用到当前系统中。

部分发行版 limits.conf 无效

部分发行版在执行 ulimit -n 1000000时,会返回以下错误提示:

ulimit: open files: cannot modify limit: Operation not permitted

又或者重启后, limits.conf 配置的值无效,这时,可能要修改以下两个文件:

  • /etc/systemd/system.conf
  • /etc/systemd/user.conf

修改这两个文件的 DefaultLimitNOFILE 值,格式为:{soft_nofile}:{hard_nofile},如改成:

DefaultLimitNOFILE=1000000:1000000

这两个文件修改后,重启服务器。

IP、端口规划

由于 可用的端口数 = 规划的IP数 * 规划的端口数。所以,要增加可用的端口数,一方面要增加IP,另一方面要指定尽可能多的端口归转发服务管理使用。

增加IP

增加IP的方法每个发行版不太一样,具体请查阅相关文档。要提请注意的是:大量外发时,路由器防火墙等设备也要增加公网IP。

服务器增加了IP以后,在转发服务的连接设置中将这些IP填入"IP地址"栏上。

端口规划

Linux系统将TCP端口分为三个部分:

  • 系统端口,1-1023,这部分由系统应用或知名应用监听使用,使用这个范围的端口需要root权限或被授予了 CAP_NET_BIND_SERVICE 能力。
  • 用户端口,1024及以上,在动态端口范围之外的端口,这部分由用户的其他应用监听使用。
  • 动态端口,在整个端口范围的后部,如:32768-60999,没有绑定端口的外出连接或监听使用这个范围的端口。

以下是部分Linux发行版的默认端口划分:

System Port Ranges

假设我们要将接入端口安排在10000以下,将10000-59999共5万个端口划为转发服务管理,为动态端口留下5536个端口:

Custom Port Ranges

经过这样的端口规划,又假如服务器共分配了5个IP,那么转发服务就可以管理和使用:5 * 50000 = 250000 个端口了。这个端口数量,在Linux系统中,可以 支持每秒发生4100多个连接的发起和关闭(由于接入端连入和连接关闭引起)。

规划好后,一方面要通过 系统参数 net.ipv4.ip_local_port_range 来设置动态端口范围(上例是60000-65535,设置方法见后文),另一方面, 在转发服务的连接设置中要将10000-59999的端口范围填入"本地端口范围"中(填入值为 10000,50000)。

TCP参数调优

Linux内核定义了很多TCP相关的参数变量,通过修改这些变量,可以调整TCP栈的行为。

查看TCP参数的方法

查看时,将要查看的变量名转换成/proc/sys/下的目录和文件,然后用 cat 命令进行查看。 如:ip_local_port_range的变量名为 net.ipv4.ip_local_port_range, 对应的系统目录为:/proc/sys/net/ipv4/ip_local_port_range,查看命令为:

cat /proc/sys/net/ipv4/ip_local_port_range

修改TCP参数的方法

修改这些参数时,通过修改 /etc/sysctl.conf 文件中对应的项进行修改。如果没有这个项,添加到文件尾部即可。 该文件格式为键值对格式,注释行以 # 符号开头。

如设置参数 net.ipv4.ip_local_port_range 为 60000 至 65535,修改或追加以下行,

ip_local_port_range=60000 65535

所有修改完成后,执行以下命令使修改(对后续连接)生效:

sudo sysctl -p

参数 net.ipv4.ip_local_port_range

该参数定义了动态端口的范围,由两个数值组成,第一个数值为最小端口号,第二个数值为最大端口号。 对于专用的转发服务器而言,服务器中不部署其他高网络负载的服务,动态端口需求很小,可以设置一个较小的范围,如分配5536个端口给动态端口。

ip_local_port_range=60000 65535

而让转发服务使用更大范围的端口。

参数 net.ipv4.tcp_max_syn_backlog, net.core.somaxconn

在Linux kernel v2.2之后,作为服务端未完成的连接的连接队列长度为 tcp_max_syn_backlog,已经完成但未被应用层accept的队列长度为somaxconn

在不同发行版中,该两选项默认值不同,适当提高可使服务器更好地应对爆发性进站连接请求,如将tcp_max_syn_backlog设为1500,将somaxconn设为4096。

参数 net.ipv4.tcp_keepalive_time, net.ipv4.tcp_keepalive_probes, net.ipv4.tcp_keepalive_intvl

这三个参数控制TCP的keepalive处理。TCP栈通过发送空的数据包,并根据是否收到应答来确认连接是否仍然保持。 keepalive另外的一个作用是一定程度上防止连接由于无来往数据包而断开。

如果转发服务的后端服务在应用层上没有在线检测机制,则可以利用TCP的keepalive来检测连接是否保持。 此时,在设置接入端时,可勾选 连接保持检测(keepalive) 选项。

然后,可以通过调整系统的这三个TCP参数来缩短检测的周期:

  • tcp_keepalive_time: 从连接空闲到触发keepalive检测的时长,单位:秒。
  • tcp_keepalive_probes: 如果一直没收到应答,在认为连接已经断开前,发出的keepalive包的次数。
  • tcp_keepalive_intvl: 重发keepalive包的时间间隔,单位:秒。

如将 tcp_keepalive_time 设置为 10 分钟,次数设为 5 次,间隔为 2秒:

tcp_keepalive_time=600
tcp_keepalive_probes=5
tcp_keepalive_intvl=2

如果连入的是终端设备,则应考虑终端设备的最大上报数据周期。tcp_keepalive_time应该设置为这个上报数据周期的两倍再加一秒或两秒。

参数 net.ipv4.tcp_orphan_retries, net.ipv4.tcp_fin_timeout

这两个是次要参数。如果转发服务主动关闭前端或后端连接,此时连接进入FIN-WAIT-1状态,等待对方的ACK包。 如果由于对方已经关闭或已经退出,不能发回ACK包,则TCP栈将尝试 tcp_orphan-retries 次的链路检测。 这个链路检测的间隔采用指数补偿算法(exponential backoff)递增。 注意,当我们查看到tcp_orphan-retries为0时,它实际是8。

连接在FIN-WAIT-1状态时,一旦收到ACK,则连接进入FIN-WAIT-2状态,等待对方发来FIN。 如果等待对方发来FIN的时间超过 tcp_fin_timeout,则TCP栈关闭连接,从而释放端口。一般情况下, 如果前端的连接真的已经断开或前端的设备停止响应,TCP会在 tcp_fin_timeout 的时间后正式关闭连接,从而释放端口。

适当减少 tcp_fin_timeout 可加快这种情况下的端口回收。这个值默认是60秒。 使用转发服务管理端口时,这个参数不能大于60秒。减少这个值会使转发服务在使用动态端口时,加快系统对动态端口的回收。