Назад | Перейти на главную страницу

В Linux, как я могу узнать, сколько эфемерных портов осталось доступным?

Есть ли в Linux способ проверить, сколько эфемерных портов осталось доступным? Иногда я вижу ошибки «Адрес уже используется» в результате исчерпания временных портов. Перезагрузка машины решит эту проблему, но было бы лучше поймать ее до того, как это произойдет.

Диапазон временных портов указан в /proc/sys/net/ipv4/ip_local_port_range. Вероятно, вы можете расширить его, чтобы он работал с 16k до 64k.

Вы можете увидеть количество открытых подключений, используя netstat -an. Сокеты могут застрять в состоянии TIME_WAIT, если вы открываете и закрываете много соединений. В некоторых местах это неизбежно, но вам может потребоваться подумать, нужен ли вам пул соединений, если это так.

Если проблема в TIME_WAIT, вы можете установить net.ipv4.tcp_tw_reuse / net.ipv4.tcp_tw_recycle для ускорения оборота соединения.

Имейте в виду, что это ограничение применяется к уникальному кортежу (исходный IP, одноранговый IP, одноранговый порт). Поэтому вам нужно будет сгруппировать вывод netstat/ss по каждому из этих кортежей и проверьте, насколько близка каждая группа к пределу подключения.

Эта почта более подробно объясняет, как можно выполнить эту группировку. Чтобы проверить, насколько каждая группа близка к пределу в Ruby, вы можете обработать ss вывод вроде:

#!/usr/bin/ruby

first_port, last_port = IO.read('/proc/sys/net/ipv4/ip_local_port_range').split.map(&:to_i)
ephemeral_port_max = last_port - first_port + 1
ephemeral_port_warning = ephemeral_port_max / 3 * 2

conns = `ss --numeric --tcp state connected "( sport >= :#{first_port} and sport <= :#{last_port} )"`

groups = Hash.new(0)
conns.lines.each do |conn|
  state, recvq, sendq, local, peer = conn.split
  local_ip, local_port = local.split(':')
  group = [local_ip, peer]
  groups[group] += 1
end

groups_requiring_warning =
  groups.select { |k, v| v > ephemeral_port_warning }
  .to_a
  .sort_by { |v1, v2| v1[1] <=> v2[1] } # Sort groups in descending order of number of connections

groups_requiring_warning.each do |group, used_port_count|
  puts "Connections from #{group[0]} to #{group[1]} "\
    "have used #{used_port_count} ephemeral ports out of #{ephemeral_port_max} max"\
    "(#{((used_port_count.to_f / ephemeral_port_max) * 100).round(2)}% used)"
end