【问题标题】:Ruby TCPServer performance issueRuby TCPServer 性能问题
【发布时间】:2026-02-23 22:10:01
【问题描述】:

我在使用 Ruby TCPServer 时遇到了一个有趣的问题,一旦客户端连接,它会持续使用越来越多的 CPU 处理能力,直到达到 100%,然后整个系统开始陷入困境,无法处理传入的数据。

有问题的处理类被设计为一个 TCP 客户端,它从嵌入式系统接收数据,对其进行处理,然后返回处理后的数据以供进一步使用(由其他类似的数据处理器,或输出到用户)。

在这种特殊情况下,有一段外部代码想要处理过的数据,但无法从主父代码(原始流程类将数据返回到的地方)访问它。这个外部部件在运行时可能会或可能不会在任何时候连接。

为了解决这个问题,我用 TCPServer 设置了一个 Thread,处理类不断地添加到一个队列中,Thread 从队列中拉出来发送给客户端。

效果很好,除了性能问题。我很好奇我的代码中是否有一些奇怪的地方,或者这只是这种方法的本质,它永远不会有足够的性能来工作。

提前感谢您对此问题的任何见解/建议!

这是我的代码/设置,以及一些测试助手:

process_data.rb

require 'socket'

class ProcessData

  def initialize
    super

    @queue = Queue.new
    @client_active = false

    Thread.new do
      # Waiting for connection
      @server = TCPServer.open('localhost', 5000)

      loop do

        Thread.start(@server.accept) do |client|
          puts 'Client connected'

          # Connection established
          @client_active = true

          begin
            # Continually attempt to send data to client
            loop do

              unless @queue.empty?
                # If data exists, send it to client
                begin
                  until @queue.empty?
                    client.puts(@queue.pop)
                  end
                rescue Errno::EPIPE => error
                  # Client disconnected
                  client.close
                end
              end
              sleep(1)
            end

          rescue IOError => error
            # Client disconnected
            @client_active = false
          end
        end # Thread.start(@server.accept)
      end # loop do
    end # Thread.new do

  end



  def read(data)
    # Data comes in from embedded system on this method

    # Do some processing
    processed_data = data.to_i + 5678 

    # Ready to send data to external client
    if @client_active
      @queue << processed_data
    end

    return processed_data
  end

end

test_embedded_system.rb(原始数据来源)

require 'socket'

@data = '1234'*100000 # Simulate lots of data coming ing

embedded_system = TCPServer.open('localhost', 5555)

client_connection = embedded_system.accept
loop do
  client_connection.puts(@data)
  sleep(0.1)
end

parent.rb(这将创建/调用 ProcessData 类)

require_relative 'process_data'

processor = ProcessData.new
loop do
  begin
    s = TCPSocket.new('localhost', 5555)
    while data = s.gets
      processor.read(data)
    end
  rescue => e
    sleep(1)
  end
end

random_client.rb(需要来自 ProcessData 的数据)

require 'socket'

loop do
  begin
    s = TCPSocket.new('localhost', 5000)
    while processed_data = s.gets
      puts processed_data
    end
  rescue => e
    sleep(1)
  end
end

要在 linux 中运行测试,请打开 3 个终端窗口:

窗口 1:./test_embedded_system.rb

窗口 2:./parent.rb

\CPU使用稳定

窗口 3:./random_client.rb

\CPU 使用率持续增长

【问题讨论】:

  • 您提供的内容是否会重现 CPU 问题?你能分享你的 ruby​​ 版本吗
  • Ruby版本:ruby 2.3.4p301 (2017-03-30 revision 58214) [x86_64-linux] 让我看看能不能整理出一个好的方法来测试一下,相信这是正确的重现问题的代码,但这绝对不是一个简单的设置
  • @Anthony 我在问题中添加了一个测试,如果有帮助请告诉我!

标签: ruby multithreading tcp tcpclient tcpserver


【解决方案1】:

我最终找出了问题所在,但不幸的是,我用我的例子误导了人们。

事实证明,我的示例并没有完全解决我遇到的问题,主要区别在于 sleep(1) 不在我的 process_data.rb 版本中。

睡眠实际上非常重要,因为它在 loop do 内部,没有睡眠,线程不会产生 GVL,并且会不断消耗 CPU 资源。

本质上,它与 TCP 的东西无关,更多地与线程和循环有关。

如果您稍后偶然发现这个问题,如果您不想让它等待,但您希望它产生 GVL,则可以在循环中添加 sleep(0)

请查看这些答案以获取更多信息: Ruby infinite loop causes 100% cpu load

sleep 0 has special meaning?

【讨论】: