由于Mac OS X上的“java.net.SocketException Invalid argument”,Tomcat启动失败

我们有一个在Tomcat 6上运行的应用程序(确切地说是6.0.35.0),由于Catalina.await方法中的socketAccept调用抛出SocketException,我们在Mac OS上的大多数工程师都遇到启动Tomcat的问题:

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 w / Java 1.7上已经存在,在过去的几个月中,我们很多人已经转向Macbook Pros。 到目前为止,唯一的症状是来自Tomcat的偶尔零字节响应,因为这个exception也被抛出在socketRead上。 错误没有打到日志,我们单独耸了耸肩作为一个孤立的问题,只有在启动问题开始时才找到原因并且我设置了一个SocketException断点:

 Daemon Thread [http-8080-1] (Suspended (breakpoint at line 47 in SocketException)) SocketException.(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内省/单例开销)似乎是影响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 OS更新。 Mac OS 10.7.x和10.8.0到1.8.3受到影响
  • 文件描述符限制 – 从5000增加到10000
  • 在主以太网接口上完全禁用IPv6
  • 设置断点,并删除受SocketException影响的第一个上下文(它们是对Web服务的传出HTTP调用)。 没变
  • 配置/etc/hosts以便机器主机名解析为localhost,并配置JVM选项以优先选择IPv4并且喜欢IPv6地址(此答案: https : //stackoverflow.com/a/16318860/364206 )

对于那些对主机配置感兴趣的人,它与默认配置相同。 我可以在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代码调查

由于问题的明显时间敏感性,设置断点以解决问题导致它不会发生。 根据评论中的要求,我还为SocksSocketImpl(PlainSocketImpl).socketAccept(SocketImpl)捕获了arg0 ,似乎没什么特别的。

 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 

我认为抛出exception的所有线程都是早期调用的受害者,一个不会导致SocketException的线程,所以我无法捕获它。 能够通过减少启动时间来启动Tomcat使我相信触发器可能是一些执行基于套接字的操作的计划任务,然后影响其他套接字操作。

这并没有解释为什么这会影响几个线程,无论我们做什么导致这种情况,神秘的SocketExceptions不应该从本机代码冒出来并在多个线程上同时导致这些exception – 也就是说,两个线程进行传出Web服务调用,Tomcat等待调用,以及多个TP处理器线程重复。

JNI代码调查

给定通用消息,我假设必须从socketAccept JNI代码中的一个系统调用返回EINVAL错误,因此我跟踪导致exception的系统调用; 任何系统调用都没有返回EINVAL 。 所以,我去OpenJDK源寻找socketAccept代码中的条件,该代码将设置然后抛出一个EINVAL ,但是我也找不到任何将errnoEINVAL代码,或者调用NET_ThrowByNameWithLastErrorNET_ThrowCurrentNET_ThrowNew的方式会抛出一个带有此默认错误消息的SocketException。

就系统调用而言,我们似乎没有达到接受系统调用:

  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 

所以,我认为问题出现在socketAccept中的accept循环顶部的超时处理代码中,但我找不到NET_TimeouterrnoEINVAL任何情况,并导致抛出此SocketException。 我指的是这段代码; 我假设jdk7u分支大部分是Oracle JDK中的东西:

  • http://hg.openjdk.java.net/jdk7u/jdk7u/jdk/file/d4bf5c15837c/src/solaris/native/java/net/PlainSocketImpl.c
  • http://hg.openjdk.java.net/jdk7u/jdk7u/jdk/file/d4bf5c15837c/src/solaris/native/java/net/bsd_close.c
  • http://hg.openjdk.java.net/jdk7u/jdk7u/jdk/file/d4bf5c15837c/src/solaris/native/java/net/net_util_md.c

救命!

我无法在Mac OS上找到受此特定问题影响的外界人士,但几乎每个人都受到影响。 必须有一些应用程序配置有所贡献,但我已经用尽了我能想到的每个途径来找到根本原因。

有关故障排除或对可能原因的见解的指示将非常感激。

您是否尝试过使用-Xcheck:jni 打开JNI调试 ? 有趣的是, Oracle文档使用PlainSocketImpl.socketAccept错误作为何时使用它的示例。

另请注意, Bug 7131399的含义是JNI在大多数平台上使用poll() ,但由于Mac上的poll()问题而在Mac OS上使用select() 。 所以也许select()也被打破了。 进一步挖掘,如果“ndfs大于FD_SETSIZE并且未定义_DARWIN_UNLIMITED_SELECT,则select()将返回EINVAL”。 FD_SETSIZE是1024,听起来你有大量的应用程序加载,所以也许所有filter一直在等待更多的1024个FD。

要获得额外的功劳,请查看您的计算机上是否已修复相关(假设已修复)的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秒后关闭文件句柄,这解释了为什么设置断点可以防止问题发生。

我有完全相同的问题(使用Tomcat7),当我在Eclipse中运行tomcat时,似乎对我有用的是勾选“发布模块上下文以分离XML文件”选项。 你已经试过了吗?

我在另一个背景下一直在与这个问题作斗争。 来自不同来源的解决方案如下所示:

  • 使用下一个覆盖更新/ 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

祝你好运!