实现小型微服务架构有以下系统架构:
2个Tomcat8实例在同一台机器上运行,InstanceA在端口8080上运行,InstanceB在端口8081上运行。
它们通过Websocket-connection (javax.websocket.server.ServerEndpoint
)连接,
InstanceA是Websocket-Client,InstanceB是WebSocketServer。
实现ServletContextListener接口在tomcat启动时调用contextInitialized(ServletContextEvent arg0)
方法。当调用此方法时,我通过
ContainerProvider.getWebSocketContainer().connectToServer(MyClientEndpoint.class, new URI(arService.getProtocol() + "://" + arService.getIp() + ":" + arService.getPort() + arService.getEndPointUrl()));
连接工作完全正常但是当我关闭InstanceA时,我得到以下StackTrace:
WARNING: The web application [AuronGateway] appears to have started a thread named [Thread-5] but has failed to stop it. This is very likely to create a memory leak. Stack trace of thread:
sun.misc.Unsafe.park(Native Method)
java.util.concurrent.locks.LockSupport.park(Unknown Source)
java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(Unknown Source)
java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(Unknown Source)
java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(Unknown Source)
java.util.concurrent.ThreadPoolExecutor.getTask(Unknown Source)
java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
java.lang.Thread.run(Unknown Source)
奇怪的是,我只能在使用Oracle JDK的Microsoft Windows(在Win7和Win10上测试它 - 同样的警告)上收到此警告。 使用OpenJDK在Ubuntu14.04上部署相同的代码时,我没有收到警告。 我使用Eclipse在两个系统上部署WebApplication和Java 8 / Tomcat8。
在调试应用程序时,我注意到websocket-Connection正确关闭。
修改 在websocketServer-Side(InstanceB)上我在onError方法中得到以下异常:
java.io.IOException: Eine vorhandene Verbindung wurde vom Remotehost geschlossen
at sun.nio.ch.SocketDispatcher.write0(Native Method)
at sun.nio.ch.SocketDispatcher.write(Unknown Source)
at sun.nio.ch.IOUtil.writeFromNativeBuffer(Unknown Source)
at sun.nio.ch.IOUtil.write(Unknown Source)
at sun.nio.ch.SocketChannelImpl.write(Unknown Source)
at org.apache.tomcat.util.net.NioChannel.write(NioChannel.java:124)
at org.apache.tomcat.util.net.NioSelectorPool.write(NioSelectorPool.java:183)
at org.apache.coyote.http11.upgrade.NioServletOutputStream.doWriteInternal(NioServletOutputStream.java:94)
at org.apache.coyote.http11.upgrade.NioServletOutputStream.doWrite(NioServletOutputStream.java:61)
at org.apache.coyote.http11.upgrade.AbstractServletOutputStream.writeInternal(AbstractServletOutputStream.java:165)
at org.apache.coyote.http11.upgrade.AbstractServletOutputStream.write(AbstractServletOutputStream.java:132)
at org.apache.tomcat.websocket.server.WsRemoteEndpointImplServer.onWritePossible(WsRemoteEndpointImplServer.java:99)
at org.apache.tomcat.websocket.server.WsRemoteEndpointImplServer.doWrite(WsRemoteEndpointImplServer.java:80)
at org.apache.tomcat.websocket.WsRemoteEndpointImplBase.writeMessagePart(WsRemoteEndpointImplBase.java:452)
at org.apache.tomcat.websocket.WsRemoteEndpointImplBase.startMessage(WsRemoteEndpointImplBase.java:340)
at org.apache.tomcat.websocket.WsRemoteEndpointImplBase.startMessageBlock(WsRemoteEndpointImplBase.java:272)
at org.apache.tomcat.websocket.WsSession.sendCloseMessage(WsSession.java:586)
at org.apache.tomcat.websocket.WsSession.doClose(WsSession.java:488)
at org.apache.tomcat.websocket.server.WsHttpUpgradeHandler.onError(WsHttpUpgradeHandler.java:150)
at org.apache.tomcat.websocket.server.WsHttpUpgradeHandler.access$300(WsHttpUpgradeHandler.java:48)
at org.apache.tomcat.websocket.server.WsHttpUpgradeHandler$WsReadListener.onError(WsHttpUpgradeHandler.java:211)
at org.apache.tomcat.websocket.server.WsHttpUpgradeHandler$WsReadListener.onDataAvailable(WsHttpUpgradeHandler.java:194)
at org.apache.coyote.http11.upgrade.AbstractServletInputStream.onDataAvailable(AbstractServletInputStream.java:198)
at org.apache.coyote.http11.upgrade.AbstractProcessor.upgradeDispatch(AbstractProcessor.java:96)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:647)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1500)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1456)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Unknown Source)
EDIT2: 我发现当只连接到websocket-Server时,警告并没有显示出来。它仅在通过
发送一些文本时出现session.getBasicRemote().sendText("TestMessage");
EDIT3:
设置一个Testproject,它是WebSocketClient的实现:
Startup.java
package de.test.client;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.websocket.ContainerProvider;
import javax.websocket.DeploymentException;
import javax.websocket.Session;
public class Startup implements ServletContextListener
{
Session session = null;
@Override
public void contextDestroyed(ServletContextEvent arg0) {
System.out.println("stopped Session");
}
@Override
public void contextInitialized(ServletContextEvent arg0) {
System.out.println("try to establish Connection");
try {
this.session = ContainerProvider.getWebSocketContainer().connectToServer(TestClient.class,
new URI("ws://127.0.0.1:8081/TestServer/wsEndMessageBroker"));
session.getBasicRemote().sendText("Hello WebsocketServer");
} catch (DeploymentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (URISyntaxException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
TestClient.java
package de.test.client;
import javax.websocket.ClientEndpoint;
import javax.websocket.CloseReason;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
@ClientEndpoint
public class TestClient {
@OnOpen
public void onOpen(final Session userSession)
{
System.out.println("Connection established");
}
@OnClose
public void onClose(final Session userSession, final CloseReason reason)
{
System.out.println("close am Clientendpoint(MessageBroker-Service)");
}
@OnMessage
public void onMessage(String jsonMessage)
{
System.out.println("message: " + jsonMessage);
}
@OnError
public void onError(Throwable error)
{
error.printStackTrace();
}
}
WebSocketServer:
TestServer.java
package de.test.server;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
@ServerEndpoint("/wsEndMessageBroker")
public class TestServer
{
public TestServer()
{
System.out.println("TestServer started");
}
@OnOpen
public void onOpen(Session session)
{
System.out.println("New Connection established SessionID: " + session.getId());
}
@OnMessage
public void message(Session session, String jsonMessage)
{
System.out.println("new Message: " + jsonMessage);
}
@OnClose
public void onClose(Session session)
{
System.out.println("Closed Connection: " + session.getId());
}
@OnError
public void onError(Throwable error)
{
error.printStackTrace();
System.out.println("Connection were closed unexpected");
}
}
Log ClientSide
INFORMATION: Initializing ProtocolHandler ["http-nio-8080"]
Feb 12, 2016 9:54:29 AM org.apache.tomcat.util.net.NioSelectorPool getSharedSelector
INFORMATION: Using a shared selector for servlet write/read
Feb 12, 2016 9:54:29 AM org.apache.coyote.AbstractProtocol init
INFORMATION: Initializing ProtocolHandler ["ajp-nio-8009"]
Feb 12, 2016 9:54:29 AM org.apache.tomcat.util.net.NioSelectorPool getSharedSelector
INFORMATION: Using a shared selector for servlet write/read
Feb 12, 2016 9:54:29 AM org.apache.catalina.startup.Catalina load
INFORMATION: Initialization processed in 787 ms
Feb 12, 2016 9:54:29 AM org.apache.catalina.core.StandardService startInternal
INFORMATION: Starting service Catalina
Feb 12, 2016 9:54:29 AM org.apache.catalina.core.StandardEngine startInternal
INFORMATION: Starting Servlet Engine: Apache Tomcat/8.0.32
Feb 12, 2016 9:54:30 AM org.apache.jasper.servlet.TldScanner scanJars
INFORMATION: At least one JAR was scanned for TLDs yet contained no TLDs. Enable debug logging for this logger for a complete list of JARs that were scanned but no TLDs were found in them. Skipping unneeded JARs during scanning can improve startup time and JSP compilation time.
Feb 12, 2016 9:54:30 AM org.apache.jasper.servlet.TldScanner scanJars
INFORMATION: At least one JAR was scanned for TLDs yet contained no TLDs. Enable debug logging for this logger for a complete list of JARs that were scanned but no TLDs were found in them. Skipping unneeded JARs during scanning can improve startup time and JSP compilation time.
Versuche Verbindung aufzubauen
Connection established
Feb 12, 2016 9:54:30 AM org.apache.coyote.AbstractProtocol start
INFORMATION: Starting ProtocolHandler ["http-nio-8080"]
Feb 12, 2016 9:54:30 AM org.apache.coyote.AbstractProtocol start
INFORMATION: Starting ProtocolHandler ["ajp-nio-8009"]
Feb 12, 2016 9:54:30 AM org.apache.catalina.startup.Catalina start
INFORMATION: Server startup in 956 ms
Feb 12, 2016 9:54:34 AM org.apache.catalina.core.StandardServer await
INFORMATION: A valid shutdown command was received via the shutdown port. Stopping the Server instance.
Feb 12, 2016 9:54:34 AM org.apache.coyote.AbstractProtocol pause
INFORMATION: Pausing ProtocolHandler ["http-nio-8080"]
Feb 12, 2016 9:54:34 AM org.apache.coyote.AbstractProtocol pause
INFORMATION: Pausing ProtocolHandler ["ajp-nio-8009"]
Feb 12, 2016 9:54:34 AM org.apache.catalina.core.StandardService stopInternal
INFORMATION: Stopping service Catalina
stopped Session
Feb 12, 2016 9:54:34 AM org.apache.catalina.loader.WebappClassLoaderBase clearReferencesThreads
WARNUNG: The web application [TestClient] appears to have started a thread named [Thread-5] but has failed to stop it. This is very likely to create a memory leak. Stack trace of thread:
sun.misc.Unsafe.park(Native Method)
java.util.concurrent.locks.LockSupport.park(Unknown Source)
java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(Unknown Source)
java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(Unknown Source)
java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(Unknown Source)
java.util.concurrent.ThreadPoolExecutor.getTask(Unknown Source)
java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
java.lang.Thread.run(Unknown Source)
Feb 12, 2016 9:54:34 AM org.apache.coyote.AbstractProtocol stop
INFORMATION: Stopping ProtocolHandler ["http-nio-8080"]
Feb 12, 2016 9:54:34 AM org.apache.coyote.AbstractProtocol stop
INFORMATION: Stopping ProtocolHandler ["ajp-nio-8009"]
Feb 12, 2016 9:54:34 AM org.apache.coyote.AbstractProtocol destroy
INFORMATION: Destroying ProtocolHandler ["http-nio-8080"]
Feb 12, 2016 9:54:34 AM org.apache.coyote.AbstractProtocol destroy
INFORMATION: Destroying ProtocolHandler ["ajp-nio-8009"]
Log ServerSide
Feb 12, 2016 9:54:26 AM org.apache.coyote.AbstractProtocol init
INFORMATION: Initializing ProtocolHandler ["http-nio-8081"]
Feb 12, 2016 9:54:26 AM org.apache.tomcat.util.net.NioSelectorPool getSharedSelector
INFORMATION: Using a shared selector for servlet write/read
Feb 12, 2016 9:54:26 AM org.apache.coyote.AbstractProtocol init
INFORMATION: Initializing ProtocolHandler ["ajp-nio-8010"]
Feb 12, 2016 9:54:26 AM org.apache.tomcat.util.net.NioSelectorPool getSharedSelector
INFORMATION: Using a shared selector for servlet write/read
Feb 12, 2016 9:54:26 AM org.apache.catalina.startup.Catalina load
INFORMATION: Initialization processed in 992 ms
Feb 12, 2016 9:54:26 AM org.apache.catalina.core.StandardService startInternal
INFORMATION: Starting service Catalina
Feb 12, 2016 9:54:26 AM org.apache.catalina.core.StandardEngine startInternal
INFORMATION: Starting Servlet Engine: Apache Tomcat/8.0.32
Feb 12, 2016 9:54:27 AM org.apache.jasper.servlet.TldScanner scanJars
INFORMATION: At least one JAR was scanned for TLDs yet contained no TLDs. Enable debug logging for this logger for a complete list of JARs that were scanned but no TLDs were found in them. Skipping unneeded JARs during scanning can improve startup time and JSP compilation time.
Feb 12, 2016 9:54:27 AM org.apache.jasper.servlet.TldScanner scanJars
INFORMATION: At least one JAR was scanned for TLDs yet contained no TLDs. Enable debug logging for this logger for a complete list of JARs that were scanned but no TLDs were found in them. Skipping unneeded JARs during scanning can improve startup time and JSP compilation time.
Feb 12, 2016 9:54:27 AM org.apache.coyote.AbstractProtocol start
INFORMATION: Starting ProtocolHandler ["http-nio-8081"]
Feb 12, 2016 9:54:27 AM org.apache.coyote.AbstractProtocol start
INFORMATION: Starting ProtocolHandler ["ajp-nio-8010"]
Feb 12, 2016 9:54:27 AM org.apache.catalina.startup.Catalina start
INFORMATION: Server startup in 895 ms
TestServer started
New Connection established SessionID: 0
new Message: Hello WebsocketServer
java.io.IOException: Unable to write the complete message as the WebSocket connection has been closed
at org.apache.tomcat.websocket.WsSession.registerFuture(WsSession.java:664)
at org.apache.tomcat.websocket.FutureToSendHandler.get(FutureToSendHandler.java:92)
at org.apache.tomcat.websocket.WsRemoteEndpointImplBase.startMessageBlock(WsRemoteEndpointImplBase.java:277)
at org.apache.tomcat.websocket.WsSession.sendCloseMessage(WsSession.java:586)
at org.apache.tomcat.websocket.WsSession.doClose(WsSession.java:488)
at org.apache.tomcat.websocket.server.WsHttpUpgradeHandler.onError(WsHttpUpgradeHandler.java:150)
at org.apache.tomcat.websocket.server.WsHttpUpgradeHandler.access$300(WsHttpUpgradeHandler.java:48)
at org.apache.tomcat.websocket.server.WsHttpUpgradeHandler$WsReadListener.onError(WsHttpUpgradeHandler.java:211)
at org.apache.tomcat.websocket.server.WsHttpUpgradeHandler$WsReadListener.onDataAvailable(WsHttpUpgradeHandler.java:194)
at org.apache.coyote.http11.upgrade.AbstractServletInputStream.onDataAvailable(AbstractServletInputStream.java:198)
at org.apache.coyote.http11.upgrade.AbstractProcessor.upgradeDispatch(AbstractProcessor.java:96)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:647)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1500)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1456)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Unknown Source)
Connection were closed unexpected
Closed Connection: 0
java.io.IOException: Eine vorhandene Verbindung wurde vom Remotehost geschlossen
at sun.nio.ch.SocketDispatcher.read0(Native Method)
at sun.nio.ch.SocketDispatcher.read(Unknown Source)
at sun.nio.ch.IOUtil.readIntoNativeBuffer(Unknown Source)
at sun.nio.ch.IOUtil.read(Unknown Source)
at sun.nio.ch.SocketChannelImpl.read(Unknown Source)
at org.apache.tomcat.util.net.NioChannel.read(NioChannel.java:137)
at org.apache.coyote.http11.upgrade.NioServletInputStream.fillReadBuffer(NioServletInputStream.java:136)
at org.apache.coyote.http11.upgrade.NioServletInputStream.doRead(NioServletInputStream.java:80)
at org.apache.coyote.http11.upgrade.AbstractServletInputStream.read(AbstractServletInputStream.java:124)
at org.apache.tomcat.websocket.server.WsFrameServer.onDataAvailable(WsFrameServer.java:60)
at org.apache.tomcat.websocket.server.WsHttpUpgradeHandler$WsReadListener.onDataAvailable(WsHttpUpgradeHandler.java:186)
at org.apache.coyote.http11.upgrade.AbstractServletInputStream.onDataAvailable(AbstractServletInputStream.java:198)
at org.apache.coyote.http11.upgrade.AbstractProcessor.upgradeDispatch(AbstractProcessor.java:96)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:647)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1500)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1456)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Unknown Source)
Connection were closed unexpected
答案 0 :(得分:2)
堆栈跟踪表明创建了一个名为" Thread-5"的线程。应用程序关闭时尚未停止。这可能只是被视为警告,因为tomcat能够handle such problems。但是,每次重新部署应用程序后,如果不重新启动tomcat,它也可能是导致内存泄漏的原因。
你可以在java.lang.Thread类中设置一个条件断点,以便找出具有名称" Thread-5"的线程的位置。创建了。 在找到线程的创建者之后,您可以研究如何正常关闭创建此线程的组件。
关闭功能可以放在ServletContextListener的contextDestroyed()方法中,也可以放在Spring @PreDestroy注释的方法中。