【问题标题】:Java Websockets poor scalability despite the use of java.NIO尽管使用了 java.NIO,Java Websockets 的可扩展性很差
【发布时间】:2015-11-30 12:41:48
【问题描述】:

我尝试创建连接到我的 glassfish websocket 服务器的大约 5000 个并发客户端(从服务器到客户端的不间断数据发送)。 (CPU:双核,8 GB RAM)

连接了大约 2500 个客户端后,连接时间约为 67(!)秒,我无法连接更多客户端,原因是 TimeOutException。

一些事实:

  • 线程池最大大小设置为 12.000。
  • 在第一次出现 TimeoutException 时,我有 2500 个客户端和大约 2450 个线程。因此,我们在这里讨论每个连接一个线程。
  • 这不是内存问题!

然后在 Node.js 和 golang 中编写了两个简单的 Websocket 代理服务器来处理 websocket 连接。 Proxy Server 和 glassfish 服务器之间的数据交换通过简单的 Websocket 连接进行。

现在我能够毫无问题地创建超过 5000 个并发客户端。 Wildfly 8 和 Tomcat 8 也有同样的问题。 评价如下:

现在我的问题。 Tyrus 是 glassfish 中 Websocket 协议的实现,并在底层使用 java.nio 库进行非阻塞 I/O。

那么为什么它的可扩展性这么差呢?或者为什么可扩展性如此不同。我的意思是我看到java.nio

没有优势

附:只是调度开销?


编辑:如何重现问题

用于创建和连接客户端的客户端软件和 Websocket 服务器位于不同的 PC 上。

  1. 启动 glassfish 简单的 echo websocket 服务器。大约有 76 个活跃的玻璃鱼线。
  2. 现在连接 2000 个客户端而不启动任何通信,只需连接到服务器 - 空闲模式。就我而言,这样做没有问题。由于所有客户端都已连接,因此大约有 80 个活动线程。几乎没有变化。
  3. 使用Timer 让所有 2000 个客户端同时开始与 websocket 服务器通信。您会注意到现在的线程数量约为 2019 年。响应时间约为 6.5 秒。
  4. 最后尝试连接接下来的 500 个客户端。第一个 TimeoutException 将发生。只会连接少数客户端。
  5. 同时停止通信。

【问题讨论】:

  • 那么您的测量结果是否具有可比性?您的 node.js 和 golang 实现是否有 2450 个线程?
  • @Kayaman 没有,因为代理服务器和真实服务器之间只有一个连接。但是由于 Tyrus 使用非阻塞 IO 并且 node.js 也使用它。为什么可扩展性如此不同。我的意思是我看不到 java.nio 的优势
  • 由于您实际上没有提供任何可以继续进行的操作,因此我将为您提供“您可能做错了什么”的答案。
  • 那么 node.js 和 Go 版本呢。他们是否也产生了多个线程?
  • @StephenC node.js 和 go 版本只产生一些线程:THREAD_COUNT = CPU_COUNT

标签: java node.js multithreading go websocket


【解决方案1】:

有很多可能的答案包括:

  1. 也许 Tyrus 真的不能很好地扩展。

  2. 也许你没有以正确的方式使用它;即你的代码正在做一些导致 Tyrus 表现不佳的事情。

  3. 也许你在“比较苹果和橘子”;即您的测试正在比较不同的事物。

  4. 也许它是一个内存问题。你有什么证据证明它不是?

  5. 可能是多种原因造成的。

很遗憾,您没有提供任何具体信息来帮助我们区分可能的原因和不太可能的原因。


根据您在更新/cmets 中报告的内容,看来 Tyrus 正在为每个 WebSocket 连接使用一个线程,但其他线程使用更具可扩展性的方法。

使用 NIO 并不一定意味着非阻塞 I/O。


在用于 WebSocket API 的 Tomcat 7 实现的 documentation 中,它是这样说的:

Java WebSocket 1.0 规范要求异步写入的回调在与启动写入的线程不同的线程上执行。由于容器线程池没有通过 Servlet API 公开,WebSocket 实现必须提供自己的线程池。此线程池由以下 servlet 上下文初始化参数控制:

  • org.apache.tomcat.websocket.executorCoreSize:执行器线程池的核心大小。如果未设置,则使用默认值 0(零)。请注意,执行程序线程池的最大允许大小被硬编码为 Integer.MAX_VALUE,这实际上意味着它是无限的。
  • org.apache.tomcat.websocket.executorKeepAliveTimeSeconds:空闲线程在执行器线程池中保留的最长时间,直到它被终止。如果未指定,则使用默认的 60 秒。

这暗示了为什么在 Tyrus 中创建了线程,并暗示 Tomcat 可能比 Glassfish 上的 Tyrus 更具可扩展性。 (我也会在 Grizzly 上尝试 Tyrus。)

【讨论】:

    猜你喜欢
    • 2018-04-26
    • 1970-01-01
    • 2011-03-22
    • 2012-02-22
    • 2012-01-27
    • 2012-02-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多