Rails / Dragonfly / Apache – Rack :: Cache – comment utiliser X-Sendfile?

J’utilise Dragonfly pour servir des images traitées pour mon application Rails. Dragonfly s’appuie sur Rack :: Cache pour de futures visites sur ces images traitées, de sorte que Dragonfly n’aura pas à traiter ces images encore et encore, gaspillant ainsi du temps CPU.

Mon problème commence ici: si j’ai raison que l’envoi d’un fichier via Rack :: Cache occupe toujours un processus Rails, alors la visualisation d’une page de 30 images, même si ces images ont une taille de fichier réduite, va bien lier les processus Rails rapidement. Si plusieurs visiteurs viennent voir cette page, ils connaîtront des temps de réponse très lents. Comment puis-je recevoir ces fichiers via X-Sendfile?

J’ai défini ce qui suit dans production.rb , mais je sais que ce sont pour les actifs de Rails, pas pour les fichiers Dragonfly:

 config.serve_static_assets = false config.action_dispatch.x_sendfile_header = "X-Sendfile" 

Je sais que Rack :: Cache supporte en quelque sorte X-Sendfile (probablement via Rack :: Sendfile ) car il produit un corps qui répond à #to_path . Cependant, je ne sais pas comment activer cela. Lorsque je vérifie les fichiers provenant de Rack :: Cache, je ne vois aucune information sur X-Sendfile:

 Date: Wed, 02 Nov 2011 11:38:28 GMT Server: Apache/2.2.3 (CentOS) X-Powered-By: Phusion Passenger (mod_rails/mod_rack) 3.0.9 Content-Disposition: filename="2.JPG" Cache-Control: public, max-age=31536000 Etag: "3174d486e4df2e78a5ff9174cacbede5787d4660" X-Content-Digest: c174408eda6e689998a40db0aef4cdd2aedb3b6c Age: 28315 X-Rack-Cache: fresh Content-Length: 22377 Status: 200 Content-Type: image/jpeg 

Je sais, sur la base des articles publiés sur le net , que je suis censé voir quelque chose comme:

 X-Sendfile: /path/to/file 

En fin de compte, je ne sais pas si sa libellule ou son rack :: Cache (ou les deux) doivent être configurés. Comment puis-je obtenir Dragonfly et / ou Rack :: Cache pour servir des fichiers via X-Sendfile ?

Informations sur ma configuration:

  • Rails 3.1.1
  • Passager 3.0.9
  • CentOS
  • Le module Sendfile est installé, pour autant que je sache. J’ai XSendFile On et XSendFilePath /path/to/app spécifié dans ma configuration virtualhost, et Apache ne se plaint pas de la directive XSendFile qui XSendFile pas.

Merci!

MISE À JOUR 6 novembre 2011

Basé sur cette ancienne mise à jour , tant que Rack::Sendfile est placé devant Rack::Cache , alors X-Sendfile sera utilisé. Je l’ai fait et c’est comme ça que mon middleware ressemble . Les fichiers, cependant, n’ont toujours pas la balise X-Sendfile . Encore une fois, je ne sais pas si c’est un moyen sûr de déterminer si X-Sendfile est activé, alors j’ai vérifié la queue des passagers. Il semble que la queue soit très encombrée lorsque je visite une page.

MISE À JOUR 7 novembre 2011

Il semble que ce soit purement un problème de Rack :: Cache et Rails 3.1. Alors que Rack :: Cache prend en charge l’utilisation de X-Sendfile via Rack :: Sendfile (comme je l’ai mentionné ci-dessus, Rack :: Cache, lorsque vous utilisez le Disk EntityStore qui répond à to_path depuis que le corps qu’il retourne est une sous-classe de File ) 3.1 utilise sa propre solution de stockage. Rails 3.1 utilise ActiveSupport :: Cache :: FileStore , qui est défini par défaut si vous ne spécifiez rien dans votre fichier production.rb .

Le problème avec FileStore est que le corps renvoyé fait partie de la réponse à envoyer en amont, car ce corps ne répond pas à to_path . Le corps est une instance de ActiveSupport :: Cache :: Entry . Vous pouvez voir ici que lorsque le FileStore est invité à lire un fichier en cache, il le lit via File.open('/path/to/file') {|f| Marshal.load(f) } File.open('/path/to/file') {|f| Marshal.load(f) } qui renvoie une instance de Entry. La valeur qui est transmise au client en amont et en aval correspond à la valeur Entrée # .

Mes questions

Pour m’aider à décider si je devrais corriger cela, ou pour que Rails utilise plutôt le propre disque de stockage de Rack :: Cache, j’ai quelques questions:

  1. Quelle est la raison pour laquelle les propres solutions de stockage de Rack :: Cache n’ont pas été utilisées pour Rails 3.1? Pourquoi Rails a-t-il sa propre?
  2. Y a-t-il une raison pour laquelle Maréchal est utilisé? Y a-t-il une raison pour laquelle un stream d’octets de données doit être renvoyé à la place?

Je suis entré plus profondément que d’habitude et serai surpris si j’ai bien compris les choses. J’espère trouver une réponse!

Comme alternative à Varnish, vous pouvez utiliser le mod_disk_cache d’Apache. Ce serait moins de travail à configurer que vous utilisez déjà Apache.

J’ai fini par faire fonctionner ceci, bien qu’avec nginx & unicorn plutôt qu’Apache & Passenger.

Comme vous l’avez souligné dans votre problème Github , vous pouvez basculer Rack :: Cache pour utiliser son fichier standard: / store plutôt que les rails: / store, ce qui permettra aux réponses de répondre à to_path .

 config.action_dispatch.rack_cache = { :verbose => true, :metastore => URI.encode("file:/PATH/TO/CACHE/STORE"), :entitystore => URI.encode("file:/PATH/TO/CACHE/STORE") } 

Dragonfly le fait en développement et vous pouvez toujours le faire en production si vous le souhaitez. La mise en garde est que si vous utilisez une des fonctionnalités de mise en cache Rails qui utilisent Rack :: Cache, les entrées de la mémoire cache seront stockées dans cette mémoire plutôt que celle de Rails standard, vous devrez donc en tenir compte si vous en avez besoin. effacez n’importe laquelle de ces entrées manuellement.

Vous devez également vous assurer que vous insérez le middleware Rack :: Sendfile à l’avant de la stack avec l’argument config.action_dispatch.x_sendfile_header. Sans l’argument config, Rack :: Sendfile n’appenda pas l’en-tête.

 config.middleware.insert 0, Rack::Sendfile, config.action_dispatch.x_sendfile_header 

My Gist montre mes lignes pertinentes dans production.rb et mon modèle nginx. Devrait être facilement adapté pour fonctionner avec le module Apache X-Sendfile.

Une autre chose à noter si vous testez ceci, est que si vous envoyez uniquement une requête HEAD via cURL par exemple, vous n’obtiendrez pas l’en-tête X-Sendfile correspondant dans la réponse car Rack :: Cache n’enverra pas réellement le corps pour une requête HEAD et donc Rack :: Sendfile n’a rien à appeler to_path .