web-dev-qa-db-fra.com

Sinatra est-il multi-thread?

Sinatra est-il multi-thread? J'ai lu ailleurs où ce "sinatra est multi-thread par défaut", qu'est-ce que cela implique?

Considérez cet exemple

get "/multithread" do
  t1 = Thread.new{
    puts "sleeping for 10 sec"
    sleep 10
    # Actually make a call to Third party API using HTTP NET or whatever.
  }
  t1.join
  "multi thread"
end

get "/dummy" do
  "dummy"
end

Si j'accède à "/ multithread" et "/ dummy" par la suite dans un autre onglet ou navigateur, rien ne peut être servi (dans ce cas pendant 10 secondes) jusqu'à ce que la demande "/ multithread" soit terminée. Dans le cas où l'activité se bloque, l'application ne répond plus.

Comment pouvons-nous contourner ce problème sans générer une autre instance de l'application?

46
ch4nd4n

tl; dr Sinatra fonctionne bien avec les threads, mais vous devrez probablement utiliser un autre serveur Web.

Sinatra lui-même n'impose aucun modèle de concurrence, il ne gère même pas la concurrence. Cela se fait par le gestionnaire de rack (serveur Web), comme Thin, WEBrick ou Passenger. Sinatra lui-même est thread-safe, ce qui signifie que si votre gestionnaire de rack utilise plusieurs threads pour les demandes du serveur, cela fonctionne très bien. Cependant, puisque Ruby 1.8 ne prend en charge que les threads verts et Ruby 1.9 a un verrou global VM lock), les threads ne sont pas très répandus utilisé pour la simultanéité, car sur les deux versions, Threads ne fonctionnera pas vraiment en parallèle. Cependant, sur JRuby ou le prochain Rubinius 2.0 (les deux alternatives Ruby implémentations).

La plupart des gestionnaires de rack existants qui utilisent des threads utiliseront un pool de threads afin de réutiliser les threads au lieu de créer réellement un thread pour chaque demande entrante, car la création de thread n'est pas gratuite, en particulier. sur 1.9 où les threads mappent 1: 1 aux threads natifs. Les fils verts ont beaucoup moins de frais généraux, c'est pourquoi les fibres, qui sont essentiellement des fils verts programmés en coopération, tels qu'utilisés par la sinatra-synchronie mentionnée ci-dessus, sont devenues si populaires récemment. Vous devez savoir que toute communication réseau devra passer par EventMachine, vous ne pouvez donc pas utiliser la gemme mysql, par exemple, pour parler à votre base de données.

Les fibres s'adaptent bien pour le traitement intense du réseau, mais échouent lamentablement pour les calculs lourds. Vous êtes moins susceptible de rencontrer des conditions de concurrence, un écueil courant avec la concurrence, si vous utilisez des fibres, car elles ne font un changement de contexte qu'à des points clairement définis (avec la synchronisation, chaque fois que vous attendez les E/S). Il existe un troisième modèle de concurrence commun: les processus. Vous pouvez utiliser le serveur de préforking ou lancer plusieurs processus vous-même. Bien que cela semble une mauvaise idée à première vue, cela présente certains avantages: dans l'implémentation normale Ruby, c'est la seule façon d'utiliser simultanément tous vos processeurs. Et vous évitez l'état partagé, donc pas les conditions de concurrence par définition. De plus, les applications multiprocessus évoluent facilement sur plusieurs machines. Gardez à l'esprit que vous pouvez combiner plusieurs processus avec d'autres modèles de concurrence (par événement, coopératif, préemptif).

Le choix est principalement fait par le serveur et le middleware que vous utilisez:

  • Multi-processus, sans préforking: Mongrel, Thin, WEBrick, Zbatery
  • Multi-processus, préforking: Licorne, Rainbows, Passenger
  • Evented (adapté pour sinatra-synchrony): Thin, Rainbows, Zbatery
  • Threaded: Net :: HTTP :: Server, Threaded Mongrel, Puma, Rainbows, Zbatery, Thin [1], Phusion Passenger Enterprise> = 4

[1] depuis Sinatra 1.3.0, Thin sera démarré en mode thread, s'il est démarré par Sinatra (c'est-à-dire avec Ruby app.rb, mais pas avec la commande thin, ni avec rackup).

91
Konstantin Haase

En parcourant Google, j'ai trouvé ce joyau:

sinatra-synchronie

ce qui pourrait vous aider, car il vous touche question.

Il y a aussi une référence, ils ont fait presque la même chose que vous voulez (appels externes).

Conclusion: EventMachine est la réponse ici!

6
asaaki

J'ai pensé que je pourrais élaborer pour les gens qui rencontrent cela. Sinatra comprend ce petit morceau de code:

   server.threaded = settings.threaded if server.respond_to? :threaded=    

Sinatra détectera la gemme que vous avez installée pour un serveur Web (aka, thin, puma, peu importe.) Et si elle répond à "threaded", elle sera configurée pour être threadée si demandé. Soigné.

4
Joel Jackson

Je suis entré dans JRuby moi-même récemment et je suis extrêmement surpris de la simplicité de passer de l'IRM à JRuby. Cela implique à peu près d'échanger quelques gemmes (dans la plupart des cas).

Vous devriez jeter un œil à la combinaison JRuby et Trinidad (App Server). Torquebox semble également être une solution tout-en-un intéressante, il est livré avec bien plus qu'un simple serveur d'application.

Si vous voulez avoir un serveur d'application qui prend en charge le filetage et que vous connaissez Mongrel, Thin, Unicorn, etc., Trinidad est probablement le plus facile à migrer, car il est pratiquement identique du point de vue des utilisateurs. Aimer jusqu'ici!

1
Michael van Rooijen

Après avoir apporté quelques modifications au code, j'ai pu exécuter l'application padrino/sinatra sur mizuno . Au départ, j'ai essayé d'exécuter l'application Padrino sur jRuby, mais c'était tout simplement trop instable et je n'ai pas cherché à savoir pourquoi. J'étais confronté à des plantages JVM lors de l'exécution sur jRuby. J'ai également parcouru cet article, ce qui me fait penser pourquoi même choisir Ruby si le déploiement peut être tout sauf facile.

Y a-t-il une discussion sur le déploiement d'applications en rubis? Ou puis-je générer un nouveau fil :)

1
ch4nd4n