2011-01-10 3 views
6

Я потратил несколько дней на то, чтобы найти примеры неэхо-сервера для eventmachine, но их просто нет. Скажем, я хочу, чтобы написать сервер, который принимает файл и записывает его в TempFile:Как писать (большие) файлы с Ruby Eventmachine

require 'rubygems' 
require 'tempfile' 
require 'eventmachine' 

module ExampleServer 

    def receive_data(data) 
    f = Tempfile.new('random') 
    f.write(data) 
    ensure 
    f.close 
    end 

end 

EventMachine::run { 
    EventMachine::start_server "127.0.0.1", 8081, ExampleServer 
    puts 'running example server on 8081' 
} 

Запись в файл будет блокировать реактор, но я не понимаю, как это сделать «Eventmachine стиль». Должен ли я читать данные в кусках и записывать каждый фрагмент на диск в блоке Em.next_tick?

Спасибо за любую помощь Andreas

ответ

1

Из docs, кажется, вам просто нужно attach файл (хотя, как вы отмечаете, что не может быть действительным, кажется вариантом является использование file.write/т.е. блокировка ...) и send_data.

Хотя я думал, что вы не можете смешивать блокирование/неблокирующая IO с EM :(

Учитывая исходные данные сокет, я думаю, что будет обрабатываться EventMachine.

Возможно вопрос для google group ...

~ Крисом

+0

Крис, спасибо за ваш ответ. [Этот ответ] (http://stackoverflow.com/questions/2749503/what-is-the-best-way-to-read-files-in-an-eventmachine-based-app) через tmm1 конфликтует с тем, что документы скажем (или я неправильно понимаю). В основном я пытаюсь сделать очень простой перенос файлов (чтение из сокета и запись его на диск) ... – Andreas

0

Это очень похоже на What is the best way to read files in an EventMachine-based app? (но я хотел бы знать, как эффективно читать файлы). Там, кажется, не быть любой неблокирующая файл API, так го Лучше всего вы можете написать короткие всплески с next_tick или отложить запись (с помощью defer), чтобы она работала в отдельном потоке (но я не знаю, насколько это возможно для этого решения).

1

К сожалению, файлы не очень хорошо реагируют на выбор интерфейсов. Если вам нужно что-то более эффективное, чем IO # write (что маловероятно), вы можете использовать EIO.

EIO действительно будет только слегка разблокировать реактор и предоставить вам небольшую буферизацию. Если определенные задержки являются проблемой или у вас действительно медленные диски, это может быть полезно. В большинстве других случаев это, вероятно, всего лишь куча усилий для небольшого преимущества.

3

Два ответа:

Ленивый Ответ: просто использовать блокирующий запись. EM уже передает вам дискретные куски данных, а не одну гигантскую строку. Таким образом, ваш пример реализации может быть немного выключен. Вы уверены, что хотите создать новый временный файл для каждого отдельного фрагмента, который EM вам нравится? Тем не менее, я продолжу с предположения, что ваш пример кода работает по назначению.

Понятно, что ленивый подход зависит от устройства, на которое вы пишете, но попытка одновременного написания нескольких больших потоков на диск будет одним из основных узких мест, и вы потеряете преимущества наличия события основанный сервер. Вы просто закончите работу с жонглирующим диском во всем мире, производительность IO резко упадет, и производительность вашего сервера тоже будет. Работа с множеством вещей сразу остается в порядке с ОЗУ, но как только вы начнете работать с блочными устройствами и планированием ввода-вывода, вы столкнетесь с узкими местами производительности, независимо от того, что вы делаете.

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

Использование defer.

require 'rubygems' 
require 'tempfile' 
require 'eventmachine' 

module ExampleServer 

    def receive_data(data) 
    operation = proc do 
     begin 
     f = Tempfile.new('random') 
     f.write(data) 
     ensure 
     f.close 
     end 
    end 

    callback = proc do 
     puts "I wrote a file!" 
    end 

    EM.defer(operation, callback) 
    end 

end 

EventMachine::run { 
    EventMachine::start_server "127.0.0.1", 8081, ExampleServer 
    puts 'running example server on 8081' 
} 

Да, это использует резьбу. В этом случае это не так уж плохо: вам не нужно беспокоиться о синхронизации между потоками, потому что EM достаточно хорош, чтобы справиться с этим для вас. Если вам нужен ответ, используйте обратный вызов, который будет выполняться в потоке основного реактора, когда рабочий поток завершится. Кроме того, GIL является чем-то вроде не-проблемы для этого случая, поскольку вы имеете дело с блокировкой ввода-вывода здесь и не пытаетесь добиться параллелизма между процессорами.

Но если вы намеревались написать все в один файл, вам придется быть осторожным с отсрочкой, так как проблема синхронизации возникнет, так как ваши потоки, скорее всего, попытаются записать в один и тот же файл одновременно.