Когда я выполняю netstat -tulnp
, вывод выглядит следующим образом:
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 127.0.0.11:43043 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.1:8005 0.0.0.0:* LISTEN 1/java
tcp 0 0 0.0.0.0:2021 0.0.0.0:* LISTEN 1/java
tcp 0 0 0.0.0.0:22222 0.0.0.0:* LISTEN 1/java
tcp 0 0 0.0.0.0:8719 0.0.0.0:* LISTEN 1/java
tcp 101 0 0.0.0.0:80 0.0.0.0:* LISTEN 1/java
tcp 51 0 0.0.0.0:1234 0.0.0.0:* LISTEN 1/java
tcp 0 0 0.0.0.0:20891 0.0.0.0:* LISTEN 1/java
udp 0 0 127.0.0.11:55285 0.0.0.0:* -
Ценность Recv-Q привлекла мое внимание. После моего расследования я обнаружил, что OOM произошел в приложении JVM, и в журнале можно найти, что поток http-nio-80-Acceptor-0, отвечающий за мониторинг порта 80, вышел, и поток, отвечающий за диспетчеризацию порта 1234. запросы вышли. Соответствующие журналы следующие:
Exception: java.lang.OutOfMemoryError thrown from the UncaughtExceptionHandler in thread "http-nio-80-Acceptor-0"
Exception: java.lang.OutOfMemoryError thrown from the UncaughtExceptionHandler in thread "Thread-5"
Конфигурация по умолчанию, используемая tomcat, то есть отставание равно 100, а исходный код находится по адресу tomcat/AbstractEndpoint.java в 8.5.59 · apache/tomcat · GitHub:
/**
* Allows the server developer to specify the acceptCount (backlog) that
* should be used for server sockets. By default, this value
* is 100.
*/
private int acceptCount = 100;
public void setAcceptCount(int acceptCount) { if (acceptCount > 0) this.acceptCount = acceptCount; }
public int getAcceptCount() { return acceptCount; }
Прослушивание порта 1234 инициируется и создается HTTPServer, и созданный код HttpServer.create(new InetSocketAddress(PROMETHEUS_SERVER_PORT), 0);
, отставание исправлено до 50 в ServerSocket.java
, исходный код находится по адресу jdk/ServerSocket.java в jdk8-b120 · openjdk/jdk · GitHub:
public void bind(SocketAddress endpoint, int backlog) throws IOException {
if (isClosed())
throw new SocketException("Socket is closed");
if (!oldImpl && isBound())
throw new SocketException("Already bound");
if (endpoint == null)
endpoint = new InetSocketAddress(0);
if (!(endpoint instanceof InetSocketAddress))
throw new IllegalArgumentException("Unsupported address type");
InetSocketAddress epoint = (InetSocketAddress) endpoint;
if (epoint.isUnresolved())
throw new SocketException("Unresolved address");
if (backlog < 1)
backlog = 50;
try {
SecurityManager security = System.getSecurityManager();
if (security != null)
security.checkListen(epoint.getPort());
getImpl().bind(epoint.getAddress(), epoint.getPort());
getImpl().listen(backlog);
bound = true;
} catch(SecurityException e) {
bound = false;
throw e;
} catch(IOException e) {
bound = false;
throw e;
}
}
Из netstat(8) — справочная страница Linux мы знаем, что Recv-Q указывает текущий отставание синхронизации, когда сокет находится в состоянии Listening
, что меня смущает, так это то, почему Recv-Q на один больше, чем значение отставания, которое мы установили?
@user207421 user207421 Мой вопрос в том, почему значение Recv-Q на единицу больше, чем предустановленное невыполненное задание, а не равно невыполненному. Почему значения Recv-Q равны 101 и 51 вместо 100 и 50?
Я смог воспроизвести это поведение в программе на C и используя ss
вместо telnet. присматриваюсь к нему дальше...
TL;DR netstat/ss сообщает о полных и незавершенных соединениях, в то время как отставание касается только завершенных соединений.
we know, Recv-Q indicates current syn backlog when socket is in Listening state,
Да, это подтверждено на странице sock_diag
человек. Похоже, что netlink берет это значение из структуры ядра следующий:
rql.udiag_rqueue = sk->sk_receive_queue.qlen;
Глядя на qlen
больше, похоже, что это не то же самое, что отставание. qlen включает полные и незавершенные соединения, в то время как отставание относится только к завершенным соединениям. Страница руководства listen
отмечает это:
The behavior of the backlog argument on TCP sockets changed with Linux 2.2. Now it specifies the queue length for completely established sockets waiting to be accepted, instead of the number of incomplete connection requests.
Что касается того, почему очередь приема заботится о незавершенных соединениях, я могу только предположить, что она пытается обработать пограничный случай освобождения невыполненной работы, в то время как рукопожатие TCP все еще происходит.
A backlog value of N really does mean allow "N + 1" connections to queue to a listening socket. This allows one to specify "0" as the backlog and still get 1 connection.
Итак, у вас закончилась память, поэтому ваш принимающий поток перестал принимать, поэтому очередь невыполненных работ выросла до максимального размера. Здесь нужно решить OOM, а не невыполненную очередь.