Запуск 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, но почти все здесь затронуты. Должна быть какая-то конфигурация приложения, которая способствует, но я исчерпал все возможности, которые я могу придумать, чтобы найти основную причину.
Указатели на устранение неполадок или понимание возможной причины были бы очень оценены.
Ответы
Ответ 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. Вы уже пробовали это?
Ответ 3
Получить OpenJDK с исправлением:
http://www.java.net/download/jdk7u60/archive/b15/binaries/jdk-7u60-ea-bin-b15-macosx-x86_64-16_apr_2014.dmg
Работал для меня!
Ответ 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
Удачи!