Подтвердить что ты не робот

Запуск Tomcat завершился неудачно из-за "java.net.SocketException Недопустимый аргумент" в Mac OS X

У нас есть приложение, которое работает на Tomcat 6 (6.0.35.0, если быть точным), и большинство наших инженеров на Mac OS испытывают проблемы с запуском Tomcat из-за вызова socketAccept в методе Catalina.await, бросающего SocketException:

SEVERE: StandardServer.await: accept:
java.net.SocketException: Invalid argument
      at java.net.PlainSocketImpl.socketAccept(Native Method)
      at java.net.PlainSocketImpl.socketAccept(PlainSocketImpl.java)
      at java.net.AbstractPlainSocketImpl.accept(AbstractPlainSocketImpl.java:398)
      at java.net.ServerSocket.implAccept(ServerSocket.java:522)
      at java.net.ServerSocket.accept(ServerSocket.java:490)
      at org.apache.catalina.core.StandardServer.await(StandardServer.java:431)
      at org.apache.catalina.startup.Catalina.await(Catalina.java:676)
      at org.apache.catalina.startup.Catalina.start(Catalina.java:628)
      at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
      at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
      at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
      at java.lang.reflect.Method.invoke(Method.java:601)
      at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:289)
      at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:414)
      at mycompany.tomcat.startup.ThreadDumpWrapper.main(ThreadDumpWrapper.java:260)
      at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
      at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
      at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
      at java.lang.reflect.Method.invoke(Method.java:601)
      at org.tanukisoftware.wrapper.WrapperStartStopApp.run(WrapperStartStopApp.java:238)
      at java.lang.Thread.run(Thread.java:722)

Это приводит к тому, что Tomcat отключается сразу после запуска (и не имеет большого количества ярости). Мы думаем, что это было с нами на протяжении всего срока действия Mac OS с Java 1.7, за последние несколько месяцев многие из нас перешли на Macbook Pros. До сих пор единственным симптомом были случайные ответы с нулевым байтом от Tomcat, из-за этого исключения также бросали на socketRead. Ошибки не попадают в журналы, и мы индивидуально пожали плечами, как изолированная проблема, и обнаружили причину только при запуске проблемы с запуском, и я установил точку останова SocketException:

Daemon Thread [http-8080-1] (Suspended (breakpoint at line 47 in SocketException))  
  SocketException.<init>(String) line: 47 
  SocketInputStream.socketRead0(FileDescriptor, byte[], int, int, int) line: not available [native method] 
  SocketInputStream.socketRead0(FileDescriptor, byte[], int, int, int) line: not available  
  SocketInputStream.read(byte[], int, int, int) line: 150 
  SocketInputStream.read(byte[], int, int) line: 121  
  InternalInputBuffer.fill() line: 735  
  InternalInputBuffer.parseRequestLine() line: 366  
  Http11Processor.process(Socket) line: 814 
  Http11Protocol$Http11ConnectionHandler.process(Socket) line: 602  
  JIoEndpoint$Worker.run() line: 489  
  Thread.run() line: 722  

Для аргументов:

arg0  FileDescriptor  (id=499)  
  fd  1097  
  useCount  AtomicInteger  (id=503) 
    value 2 
arg1  (id=502)
arg2  0 
arg3  8192  
arg4  20000 

Проблема чувствительна к времени. Увеличение времени запуска из-за изменений в приложениях (более чем Spring introspection/singleton overhead), по-видимому, является фактором, который заставляет это влиять на запуск Tomcat; точка опрокидывания составляет около 160 секунд. Мы можем смягчить эту проблему, отключив некоторые необязательные контексты, которые нам не нужны во время разработки, чтобы сократить время запуска, но я бы предпочел найти основную причину.

Конфигурация приложения

Специфика приложения слишком сложна, чтобы входить в слишком подробные сведения, но у меня есть догадка, что это может относиться к раннему связыванию, поэтому я, по крайней мере, буду перечислять прослушивающие порты на моей машине:

localhost:32000 - Java service wrapper port
*:10001         - RMI registry
*:2322          - Java debug
*:56566         - RMI
*:8180          - Tomcat HTTP connector
*:8543          - Tomcat HTTPS connector
*:2223          - Tomcat Internal HTTP connector (used for cross-server requests)
*:14131         - 'Locking' port to determine if an internal service is running
*:56571         - EhCache RMI
*:56573         - RMI
*:62616         - ActiveMQ broker
*:5001          - SOAPMonitorService
*:8109          - Tomcat shutdown port

Элементы исключены

  • Наиболее очевидное решение: -Djava.net.preferIPv4Stack=true. У меня всегда был настроен этот параметр.
  • Любое недавнее изменение конфигурации для нашей базовой конфигурации приложения, библиотек, параметров JVM (их нет)
  • Регрессия JDK. Я тестировал JDK 1.7.0_09, 11, 15, 17 и 21 (JDK, которые я установил на своей машине в течение всего времени).
  • Обновление ОС Mac. Mac OS 10.7.x и от 10.8.0 до 1.8.3 затронуты
  • Пределы дескриптора файла - увеличены с 5000 до 10000
  • Отключение IPv6 полностью на главном интерфейсе Ethernet
  • Установка контрольных точек и удаление первых контекстов, на которые влияет SocketException (они являются исходящими HTTP-вызовами веб-служб). Без изменений
  • Конфигурирование /etc/hosts, так что имя хоста компьютера разрешается на localhost и настраивает параметры JVM для предпочтения IPv4 и не предпочитает адреса IPv6 (этот ответ: qaru.site/info/187549/...)

Для тех, кто интересуется конфигурацией хостов, он аналогичен по умолчанию. Я могу воспроизвести это на Fusion VM с чистой установкой 10.8:

##
# Host Database
#
# localhost is used to configure the loopback interface
# when the system is booting.  Do not change this entry.
##
127.0.0.1   localhost
255.255.255.255 broadcasthost
::1             localhost
fe80::1%lo0 localhost

Исследование Java-кода

Из-за очевидного чувствительного к времени характера проблемы установка контрольных точек для устранения неполадок не приводит к ее возникновению. В соответствии с просьбой в комментариях я также взял arg0 для SocksSocketImpl(PlainSocketImpl).socketAccept(SocketImpl), ничего не выглядит необычным.

arg0  SocksSocketImpl  (id=460) 
  address InetAddress  (id=465) 
    canonicalHostName null  
    holder  InetAddress$InetAddressHolder  (id=475) 
      address 0 
      family  0 
      hostName  null  
  applicationSetProxy false 
  closePending  false 
  cmdIn null  
  cmdOut  null  
  cmdsock null  
  CONNECTION_NOT_RESET  0 
  CONNECTION_RESET  2 
  CONNECTION_RESET_PENDING  1 
  external_address  null  
  fd  FileDescriptor  (id=713)  
    fd  -1  
    useCount  AtomicInteger  (id=771) 
      value 0 
  fdLock  Object  (id=714)  
  fdUseCount  0 
  localport 0 
  port  0 
  resetLock Object  (id=716)  
  resetState  0 
  server  null  
  serverPort  1080  
  serverSocket  null  
  shut_rd false 
  shut_wr false 
  socket  Socket  (id=718)  
    bound false 
    closed  false 
    closeLock Object  (id=848)  
    connected false 
    created false 
    impl  null  
    oldImpl false 
    shutIn  false 
    shutOut false 
  socketInputStream null  
  stream  false 
  timeout 0 
  trafficClass  0 
  useV4 false 

Я думаю, что все потоки, в которых выбрасываются исключения, являются жертвами более раннего вызова, который не приводит к исключению SocketException, поэтому я не смог его поймать. Возможность запуска Tomcat за счет сокращения времени запуска убеждает меня в том, что триггер, вероятно, представляет собой запланированную задачу, которая выполняет операцию на основе сокетов, которая затем влияет на другие операции сокета.

Это не объясняет, как и почему это может повлиять на несколько потоков, независимо от того, что мы делаем, чтобы вызвать это условие, таинственные SocketExceptions не должны пузыриться из собственного кода и вызывать эти исключения одновременно на нескольких потоках - это, два потока, вызывающие исходящие вызовы веб-сервисов, ожидание вызова Tomcat и несколько потоков процессора TP.

Исследование кода JNI

Учитывая общее сообщение, я предположил, что ошибка из EINVAL должна быть возвращена из одного из системных вызовов в коде socketSccept JNI, поэтому я проследил системные вызовы, ведущие к исключению; там нет EINVAL, возвращаемого с любого системного вызова. Итак, я пошел в источники OpenJDK, ища условия в коде socketAccept, который бы установил, а затем выбросил EINVAL, но я также не смог найти код, который устанавливает errno в EINVAL, или вызывает NET_ThrowByNameWithLastError, NET_ThrowCurrent или NET_ThrowNew таким образом, чтобы это исключение SocketException с этим сообщением об ошибке по умолчанию.

Что касается системных вызовов, мы, кажется, не дошли до системного вызова accept:

 PID/THRD        RELATIVE   ELAPSD    CPU SYSCALL(args)    = return
 6606/0x2c750d:  221538243       5      0 sigprocmask(0x1, 0x0, 0x14D8BE100)    = 0x0 0
 6606/0x2c750d:  221538244       3      0 sigaltstack(0x0, 0x14D8BE0F0, 0x0)     = 0 0
 6606/0x2c750d:  221538836      14     10 socket(0x2, 0x1, 0x0)    = 1170 0
 6606/0x2c750d:  221538837       3      0 fcntl(0x492, 0x3, 0x4)     = 2 0
 6606/0x2c750d:  221538839       3      1 fcntl(0x492, 0x4, 0x6)     = 0 0
 6606/0x2c750d:  221538842       5      2 setsockopt(0x492, 0xFFFF, 0x4)     = 0 0
 6606/0x2c750d:  221538852       7      4 bind(0x492, 0x14D8BE5D8, 0x10)     = 0 0
 6606/0x2c750d:  221538857       5      2 listen(0x492, 0x1, 0x4)    = 0 0
 6606/0x2c750d:  221539625       6      2 psynch_cvsignal(0x7FEFBFE00868, 0x10000000200, 0x100)    = 257 0
 6606/0x2c750d:  221539633       4      1 write(0x2, "Apr 18, 2013 11:05:35 AM org.apache.catalina.core.StandardServer await\nSEVERE: StandardServer.await: accept: \njava.net.SocketException: Invalid argument\n\tat java.net.PlainSocketImpl.socketAccept(Native Method)\n\tat java.net.PlainSocketImpl.socketAcce", 0x644)    = 1604 0

Итак, я думаю, что проблема возникает в коде обработки тайм-аута в верхней части цикла accept в socketAccept, но я не мог найти случай, когда NET_Timeout установил errno в EINVAL, и в результате возникает это SocketException. Я имею в виду этот код; Я полагаю, что ветвь jdk7u по большей части является судом в Oracle JDK:

Помогите!

Я не могу найти никого во внешнем мире, затронутого этой конкретной проблемой в Mac OS, но почти все здесь затронуты. Должна быть какая-то конфигурация приложения, которая способствует, но я исчерпал все возможности, которые я могу придумать, чтобы найти основную причину.

Указатели на устранение неполадок или понимание возможной причины были бы очень оценены.

4b9b3361

Ответ 1

Вы пробовали включить отладку JNI с помощью -Xcheck:jni? Интересно, что документация Oracle использует ошибку PlainSocketImpl.socketAccept в качестве примера того, когда использовать это.

Обратите внимание также, что подразумевается Ошибка 7131399, что JNI использует poll() на большинстве платформ, но select() на Mac OS из-за к проблеме с poll() на Mac. Так что, может быть, select() тоже сломано. Копаясь немного дальше, select() вернет EINVAL, если "ndfs больше, чем FD_SETSIZE и _DARWIN_UNLIMITED_SELECT не определено". FD_SETSIZE - 1024, и похоже, что у вас много загружаемых приложений, поэтому, возможно, все это фильтрует до ожидания более 1024 FDs за один раз.

Для получения дополнительной информации см. связанная (предположительно исправленная) ошибка Java на самом деле исправлена ​​на вашем компьютере. Отчет об ошибке содержит указатели для проверки случаев.


Благодаря ответу Old Pro я подтвердил, что ограничение select() FD_SETSIZE является причиной. Я нашел существующую ошибку для этого ограничения:

https://bugs.openjdk.java.net/browse/JDK-8021820

Проблема может быть воспроизведена с помощью следующего кода:

import java.io.*;
import java.net.*;

public class SelectTest {
  public static void main(String[] args) throws Exception {
    // Use 1024 file descriptors. There'll already be some in use, obviously, but this guarantees the problem will occur
    for(int i = 0; i < 1024; i++) {
      new FileInputStream("/dev/null");
    }
    ServerSocket socket = new ServerSocket(8080);
    socket.accept();
  }
}

Почти год спустя Java 7u60 исправляет эту проблему:

http://www.oracle.com/technetwork/java/javase/2col/7u60-bugfixes-2202029.html

Я также обнаружил, что Tomcat WebappClassLoader закрывает дескрипторы файлов через 90 секунд, что объясняет, почему установка точек останова предотвратила возникновение проблемы.

Ответ 2

У меня была точно такая же проблема (с Tomcat7), и мне кажется, что для меня стоит отметить параметр "Опубликовать контекст модуля для разделения файлов XML", когда я запускаю tomcat внутри Eclipse. Вы уже пробовали это?

Ответ 4

Я боролся с этой проблемой в другом контексте. Решение (ы), объединенные из разных источников, выглядят следующим образом:

  • Обновить /etc/hosts со следующими переопределениями:
    • :: 1 EWD-MacBook-Pro.local
    • 127.0.0.1 EWD-MacBook-Pro.local localhost

(EWD-MacBook-Pro.local - мое имя машины)

и

  • Установить свойства системы:
    • java.net.preferIPv4Stack = > true
    • java.net.preferIPv6Addresses = > false

Удачи!