如何在Java中关闭WebSocketContainer/WebSocketClient/Jetty客户端?

4
我正在尝试使用Jetty的WebSocket客户端,但应用程序会卡在那里而无法退出。这并不复杂...
public class TestEndPoint extends Endpoint {
    private Session sess;

    public TestEndPoint(URI endpoint) throws Exception {

        WebSocketContainer container = ContainerProvider.getWebSocketContainer();
        container.connectToServer(this, endpoint);
    }

    public void close() {
        try {
            this.sess.close();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    ...
}

我将它连接到我的PHP Ratchet WS,没有特殊的逻辑,当客户端连接时我只是输出 open ,当客户端断开连接时输出 close

然后我像这样运行它:

public static void main(String[] args) {
    try {
        TestEndPoint test = new TestEndPoint(new URI("ws://localhost:8080/test"));
        Thread.sleep(5000);
        test.close();
    }
    catch (Exception e)
    {
        e.printStackTrace();
    }
}

我看到我的WS会打印“close”,但Java应用程序永远不会退出。所以我取了一个线程转储并看到jetty的东西在某个QueuedThreadPool中卡住了……
一些线程转储:
"WebSocketContainer@1735934726-15" #15 daemon prio=5 os_prio=0 tid=0x0000000016cdf000 nid=0x1c5c runnable [0x000000001763e000]
   java.lang.Thread.State: RUNNABLE
    at sun.nio.ch.WindowsSelectorImpl$SubSelector.poll0(Native Method)
    at sun.nio.ch.WindowsSelectorImpl$SubSelector.poll(WindowsSelectorImpl.java:296)
    at sun.nio.ch.WindowsSelectorImpl$SubSelector.access$400(WindowsSelectorImpl.java:278)
    at sun.nio.ch.WindowsSelectorImpl.doSelect(WindowsSelectorImpl.java:159)
    at sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:86)
    - locked <0x00000000ebf678c8> (a sun.nio.ch.Util$3)
    - locked <0x00000000ebf68128> (a java.util.Collections$UnmodifiableSet)
    - locked <0x00000000ebf67d40> (a sun.nio.ch.WindowsSelectorImpl)
    at sun.nio.ch.SelectorImpl.select(SelectorImpl.java:97)
    at sun.nio.ch.SelectorImpl.select(SelectorImpl.java:101)
    at org.eclipse.jetty.io.ManagedSelector$SelectorProducer.select(ManagedSelector.java:346)
    at org.eclipse.jetty.io.ManagedSelector$SelectorProducer.produce(ManagedSelector.java:299)
    at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.doProduce(EatWhatYouKill.java:179)
    at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.produce(EatWhatYouKill.java:140)
    at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.run(EatWhatYouKill.java:131)
    at org.eclipse.jetty.util.thread.ReservedThreadExecutor$ReservedThread.run(ReservedThreadExecutor.java:243)
    at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:679)
    at org.eclipse.jetty.util.thread.QueuedThreadPool$2.run(QueuedThreadPool.java:597)
    at java.lang.Thread.run(Thread.java:748)


"WebSocketContainer@1735934726-14" #14 daemon prio=5 os_prio=0 tid=0x0000000016cc7000 nid=0xfb4 waiting on condition [0x000000001753f000]
   java.lang.Thread.State: TIMED_WAITING (parking)
    at sun.misc.Unsafe.park(Native Method)
    - parking to wait for  <0x00000000ebde1df8> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
    at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:215)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:2078)
    at org.eclipse.jetty.util.BlockingArrayQueue.poll(BlockingArrayQueue.java:392)
    at org.eclipse.jetty.util.thread.QueuedThreadPool.idleJobPoll(QueuedThreadPool.java:571)
    at org.eclipse.jetty.util.thread.QueuedThreadPool.access$800(QueuedThreadPool.java:50)
    at org.eclipse.jetty.util.thread.QueuedThreadPool$2.run(QueuedThreadPool.java:634)
    at java.lang.Thread.run(Thread.java:748)

最后,我在我的Gradle中使用了这些版本的Jetty;

compile group: 'org.eclipse.jetty.websocket', name: 'websocket-api', version: '9.4.6.v20170531'
compile group: 'org.eclipse.jetty.websocket', name: 'websocket-client', version: '9.4.6.v20170531'
compile group: 'org.eclipse.jetty.websocket', name: 'javax-websocket-client-impl', version: '9.4.7.v20170914'

如何正确关闭 Websocket 或客户端?

1个回答

8

经过大量谷歌搜索和查看许多示例代码,似乎需要将WebSocketContainer手动转换为org.eclipse.jetty.util.component.LifeCycle,然后stop它才能停止。

为了停止Jetty的WebSocket客户端,您需要通过SessionWebSocketContainer执行以下操作。 因此,在我的close函数中:

try {
    Session sess = getSession();
    WebSocketContainer container = sess.getContainer();

    // Need to cast the container to Jetty's LifeCycle
    if(container != null && container instanceof LifeCycle) { 
        Logger.debug("Stopping Jetty's WebSocket Client");
        ((LifeCycle) container).stop(); 
    }

    sess.close();
} catch (Exception e) {
    Logger.error(e);
}

在Jetty官网上并没有任何关于这方面的文档可供参考(http://www.eclipse.org/jetty/documentation/9.4.7.v20170914/),请注意,此处没有任何解释。


1
这很有帮助。这可以防止本地端点对象和守护线程的泄漏。 - Bhaskar
很高兴这篇文章在两年后对某人有所帮助!! :) - codenamezero

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接