Ruby TCPServer 性能问题

Ruby TCPServer performance issue

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

有问题的处理 class 被设计为一个 TCP 客户端,它从嵌入式系统接收数据,处理它,然后 returns 处理后的数据被进一步使用(要么由其他类似的数据处理器,或输出给用户)。

在这种特殊情况下,有一段外部代码需要此处理后的数据,但无法从主父代码访问它(原始进程 class 将其数据返回到).在 运行ning.

期间,此外部部件可以随时连接也可以不连接。

为了解决这个问题,我搭建了一个带TCPServer的Thread,处理class不断的添加到一个队列中,Thread从队列中拉取发送给客户端。

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

在此先感谢 insight/suggestions 遇到此问题!

这是我的 code/setup,还有一些测试助手:

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(这就是 create/call ProcessData class)

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终端windows:

Window1:./test_embedded_system.rb

Window2:./parent.rb

\CPU使用稳定

Window3:./random_client.rb

\CPU 使用量持续增长

我最终弄清楚了问题所在,不幸的是我用我的例子误导了人们。

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

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

本质上,它与 TCP 无关,与线程和循环有关。

如果您以后偶然发现这个问题,如果您不希望它等待,但您希望它产生 GVL,您可以在循环中放置一个 sleep(0)

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