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
我在 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