La distribution de contenu avec Nginx et X-Accel

C’est la problématique du jour : comment distribuer du contenu à des utilisateurs tout en ayant la possibilité de limiter la bande passante au cas par cas, en fonction de leur niveau d’abonnement ?

Source : nginx.org

Source : nginx.org

Et oui souvenez-vous : mon projet de fin d’année (qui consiste en une solution dropbox-like) doit proposer différents niveaux d’abonnement aux clients, comprenant différents niveaux de stockage et de bande passante. J’ai mis du temps à chercher une solution pour limiter la bande passante des utilisateurs à la volée car j’avais plusieurs contraintes :

  • Les développeurs refusaient d’implémenter cette fonctionnalité côté code, ce que je comprends tout à fait. Cela n’aurait pas été très propre…
  • Les différents tutoriels proposés sur le web indiquent généralement comment brider la bande passante par IP, or cela ne me suffit pas.
  • Il faut aussi s’assurer que l’utilisateur ne puisse pas modifier sa bande passante. Ce qui impose d’éviter tout mécanisme côté client (au revoir cookies…).

Et après plusieurs mois d’acharnement, c’est finalement Nginx qui m’apporte la solution !

Au revoir PHP, bonjour Nginx et X-Accel

Le concept est simple : plutôt que d’utiliser PHP pour envoyer le fichier afin que l’utilisateur le télécharge, nous allons utiliser PHP pour qu’il demande à Nginx de faire le travail à sa place. Et ça tombe bien : délivrer du contenu statique, c’est ce que Nginx sait faire de mieux !

Un petit schéma pour que cela soit clair. Imaginons qu’Alice soit sur mon.site.com/index.php et que sur cette page se trouve un bouton pour télécharger chatons.jpg, voici ce qu’il se passerait en temps normal :

Note : c'est vraiment très schématique !

Note : c’est vraiment très schématique !

PHP montre ses limites lorsqu’il s’agit de fichiers volumineux et nous n’avons pas la maîtrise nécessaire à ma problématique sur ce type de transfert. Plutôt que d’envoyer chaton.jpg à Alice, PHP va demander à Nginx de le faire en lui transférant une requête HTTP contenant l’entête X-Accel-Redirect: /chemin/chaton.jpg.

Nginx_X-Accel_2

Gardez bien en tête que c’est vraiment très schématique. Le code PHP pour envoyer la requête HTTP à Nginx est on ne peut plus simple :

header("X-Accel-Redirect: /chemin/chaton.jpg");

On pourrait donc imaginer un fichier download.php qui attendrait patiemment qu’une requête GET lui soit adressée pour que celui-ci transmette la requête à Nginx :

// Le fichier à télécharger est passé en GET
$path = $_GET["path"];
 
//...
// Ici vos traitements
// ou tests de sécurité
//...
 
// Et la redirection interne pour fournir le fichier
header("X-Accel-Redirect: /chemin/".$path);

Alice devra utiliser l’adresse mon.site.com/download.php?path=chaton.jpg pour récupérer son image.

Et là où cette méthode devient intéressante, c’est que nous allons pouvoir passer d’autres paramètres propres à X-Accel… Sans parler du fait que cela évite d’avoir un processus / thread PHP dédié à l’envoi du fichier, ce qui allège forcément la charge serveur.

Limitation de bande passante avec X-Accel

Et cette option s’appelle X-Accel-Limit-Rate. Si j’adapte mon code précédent :

// Le fichier à télécharger est passé en GET
$path = $_GET["path"];
 
//...
// Ici vos traitements
// ou tests de sécurité
//...
 
// Et la redirection interne pour fournir le fichier
header("X-Accel-Redirect: /chemin/".$path);
header("X-Accel-Limit-Rate 100k");

Nginx_X-Accel-Limit-Rate

Alice ne pourra pas télécharger son image à plus de 100Ko/s. Problème résolu ! 🙂

Sachez que X-Accel propose d’autres options qui pourraient peut-être vous intéresser, n’hésitez pas à aller faire un tour sur la documentation. Oh et Apache dispose d’un équivalent à travers le module xsendfile.

Enfin, pour faire les choses proprement, n’oubliez pas que l’utilisation de X-Accel ne vous dispense pas de spécifier le type mime de votre fichier et le fait qu’Alice doit le télécharger, et non l’afficher dans son navigateur.


Sources :

Cet article vous a plu ? Partagez-le sur les réseaux sociaux !

Twitter Facebook Google Plus email