6 votes

Comment écrire des (gros) fichiers avec Ruby Eventmachine

J'ai passé plusieurs jours à trouver des exemples d'eventmachine sans serveur d'écho, mais il ne semble pas y en avoir. Disons que je veux écrire un serveur qui accepte un fichier et l'écrit dans un fichier temporaire :

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'
}

L'écriture dans le fichier bloquerait le réacteur, mais je ne comprends pas comment le faire à la manière d'Eventmachine. Devrais-je lire les données par morceaux et écrire chaque morceau sur le disque dans un bloc Em.next_tick ?

Merci pour toute aide Andreas

3voto

Fitzsimmons Points 691

Deux réponses :

Réponse paresseuse : utilisez simplement une écriture bloquante. L'EM vous remet déjà des morceaux discrets de données, pas une chaîne gigantesque. Donc votre exemple d'implémentation est peut-être un peu faux. Êtes-vous sûr de vouloir créer un nouveau fichier temporaire pour chaque morceau de données qu'EM vous remet ? Cependant, je vais continuer en supposant que votre exemple de code fonctionne comme prévu.

Certes, l'approche paresseuse dépend du périphérique sur lequel vous écrivez, mais essayer d'écrire simultanément plusieurs grands flux sur le disque va constituer un goulot d'étranglement majeur et vous perdrez de toute façon les avantages d'avoir un serveur basé sur les événements. Vous vous retrouverez à jongler avec des recherches de disque dans tous les sens, les performances d'E/S chuteront, tout comme les performances de votre serveur. La gestion de plusieurs choses à la fois n'est pas un problème avec la RAM, mais dès que vous commencez à vous occuper de périphériques en bloc et de la planification des E/S, vous allez rencontrer des goulots d'étranglement au niveau des performances, quoi que vous fassiez.

Cependant, je suppose que vous pourriez vouloir effectuer de longues écritures sur le disque en même temps que vous voulez des réponses à faible latence à d'autres requêtes, non lourdes en termes d'IO. Donc, peut-être la bonne réponse :

Utilice différer .

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'
}

Oui, cela utilise l'enfilage. Ce n'est pas si grave dans ce cas : vous n'avez pas à vous soucier de la synchronisation entre les threads, car EM est assez gentil pour s'en occuper pour vous. Si vous avez besoin d'une réponse, utilisez la callback, qui sera exécutée dans le thread principal du réacteur lorsque le thread du travailleur aura terminé. De plus, la GIL n'est pas un problème dans ce cas, puisque vous avez affaire à un blocage d'E/S et que vous n'essayez pas d'obtenir la concurrence du CPU.

Mais si vous avez l'intention de tout écrire dans le même fichier, vous devrez faire attention avec defer, car le problème de synchronisation se posera car vos threads tenteront probablement d'écrire dans le même fichier au même moment.

1voto

Chris Kimpton Points 2916

De la docs il semble que vous ayez juste besoin de joindre le fichier (bien que, comme vous le soulignez, cela pourrait ne pas être valable, il semble que l'option soit d'utiliser File.write/ie bloquant...) et envoyer_données .

Je croyais que l'on ne pouvait pas mélanger les entrées-sorties bloquantes/non-bloquantes avec l'EM :(

Étant donné que la source de données est un socket, je suppose que cela sera géré par EventMachine .

Peut-être une question pour le groupe google ...

~chris

1voto

raggi Points 901

Malheureusement, les fichiers ne répondent pas bien aux interfaces sélectionnées. Si vous avez besoin de quelque chose de plus efficace que IO#write (ce qui est peu probable), alors vous pouvez utiliser EIO .

L'EIO ne fera que débloquer légèrement le réacteur, et vous fournira un tout petit peu de tampon. Si des latences spécifiques sont un problème, ou si vous avez des disques vraiment lents, cela peut être utile. Dans la plupart des autres cas, c'est probablement un tas d'efforts pour peu d'avantages.

0voto

Theo Points 60103

C'est très similaire à Quelle est la meilleure façon de lire des fichiers dans une application basée sur EventMachine ? (mais je voulais savoir comment lire les fichiers efficacement). Il ne semble pas y avoir d'API de fichier non bloquante, donc le mieux que vous puissiez faire est d'écrire de courtes rafales avec next_tick ou de différer la rédaction (avec defer ) pour qu'il s'exécute dans un thread séparé (mais je ne sais pas si cette solution est très performante).

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X