Поток в памяти для Ruby

При работе с протокольными буферами реальный размер сообщения становится известен, когда весь объект записывается в IO. Поэтому я использую следующий подход: записываю объект в промежуточный поток, получаю его размер, а затем записываю все данные с заголовком, содержащим размер int, в сокет TCP.

Что мне не нравится в следующем коде, так это функция message_size, которая использует реальный файл на диске вместо потока памяти.

require 'protocol_buffers'

module MyServer
  class AuthRequest < ProtocolBuffers::Message
    required :int32, :vers, 1
    required :int32, :orgID, 2
    required :string, :password, 3
  end

  class MyServer
    def self.authenticate(socket, params)
      auth = AuthRequest.new(:vers => params[:vers], :orgID => params[:orgID], :password => params[:password])
      size = message_size(auth)
      if size.present?
        socket.write([size, 0].pack 'NN')
        auth.serialize(socket)
        socket.flush
      end
    end

    def self.message_size(obj)
      size = nil
      io = File.new('tempfile', 'w')
      begin
        obj.serialize(io)
        io.flush
        size = io.stat.size + 4
      ensure
        io.close
      end
      size
    end
  end
end

Контроллер:

require 'my_server'
require 'socket'

class MyServerTestController < ActionController::Base
  def test
    socket = TCPSocket.new('192.168.1.15', '12345')
    begin
      MyServer::MyServer.authenticate(socket, {vers: 1, orgID: 100, password: 'hello'})
    ensure
      socket.close
    end
  end
end

person Paul    schedule 21.05.2014    source источник
comment
попробуйте ruby-doc.org/stdlib-1.9.3 /libdoc/stringio/rdoc/StringIO.html   -  person Uri Agassi    schedule 21.05.2014
comment
Безопаснее использовать реальный диск вместо памяти. Буферизация в память не масштабируема, и, учитывая скорость дисков, по сравнению со скоростью Интернета, использование дискового ввода-вывода не будет узким местом, это ваше интернет-соединение. Я бы использовал Ruby Tempfile класс для этого.   -  person the Tin Man    schedule 21.05.2014
comment
@Uri Agassi: мне нужен не строковый поток, а двоичный поток.   -  person Paul    schedule 22.05.2014
comment
@the Tin Man: Что ты говоришь... Более 15 лет я использую потоки памяти в веб-приложениях, в Delphi, в .NET, и все было хорошо, но теперь, в Ruby, это оказалось большое нет-нет. Какой сюрприз...   -  person Paul    schedule 22.05.2014
comment
В Ruby нет ничего плохого, с этим нужно быть осторожным на любом языке/в любой среде. Если вы имеете дело с файлами, размер которых превышает доступный размер буфера, у вас возникнут проблемы. Из вашего описания невозможно определить, какие аппаратные ресурсы у вас есть, поэтому, поскольку SO обрабатывает запросы от людей, использующих изящные общие серверы, вплоть до производственных площадок с выделенным оборудованием, я поделился тем, что наиболее безопасно. 15 лет не имеет значения, это окружающая среда.   -  person the Tin Man    schedule 22.05.2014


Ответы (1)


Вы можете легко использовать StringIO в качестве потока памяти. . Имейте в виду, что он называется StringIO, так как реализован в строке и определенно не привязан к строковым данным — он так же легко работает с двоичными данными:

def self.message_size_mem(obj)
  size = nil
  io = StringIO.new
  begin
    obj.serialize(io)
    io.flush
    size = io.size + 4
  ensure
    io.close
  end
  size
end


auth = AuthRequest.new(:vers => 122324, orgID: 9900235, password: 'this is a test for serialization')
MyServer.message_size(auth)
# => 47
MyServer.message_size_mem(auth)
# => 47

io = StringIO.new
auth.serialize(io)
io.flush
io.string
# => "\bԻ\a\u0010ˡ\xDC\u0004\u001A this is a test for serialization"
person Uri Agassi    schedule 22.05.2014