En continuant à naviguer sur mon site, vous consentez à ce que j'utilise des cookies pour collecter les statistiques de visites. En savoir plus

#Informatique Créer un répartisseur de charge pour Shoutcast

Comment mettre en place un load-balancer très simple pour répartir vos auditeurs sur des serveurs Shoutcast multiples ? La solution se trouve ici !

Une des problématiques auxquelles j'ai dû me confronter quand j'ai installé deux serveurs audios Shoutcast pour la défunte webradio JNT, c'était la répartition de charge. Je ne voulais pas créer un bête fichier .m3u avec toutes les URLs d'écoute dedans, et laisser le choix à l'auditeur qui n'aurait pas forcément compris pourquoi JNT apparaissait plusieurs fois dans sa playlist. J'ai donc cherché pendant quelques heures sur Google comment mettre en place du load-balancing sur Shoutcast sans vraiment trouver de liens très pertinents.

Tout à coup, m'est venue l'idée de la redirection HTTP (302) ! Parfois, notre cerveau ne pense pas forcément aux solutions les plus simples du premier coup. Maîtrisant le langage PHP, et disposant déjà d'un bout de code source permettant de récupérer les tags de la musique diffusée, j'ai lancé vim, et je suis parti pour coder un mini load-balancer…

Avant de rédiger cet article, j'ai un peu amélioré les sources pour qu'elles soient plus sexy et présentables pour un tuto. Comme pour tout, elles peuvent être améliorées à souhait, mais la structure de base est là (on pourrait imaginer carrément une GUI permettant d'administrer les différents serveurs par exemple, ou au moins refactorer le script pour qu'il utilise des classes, …). Les sources sont donc disponibles sur mon GitHub, et je vais vous détailler le fonctionnement de ce petit script.

1. Où ça commence ?

Juste pour ne pas laisser de code en dehors des fonctions, j'ai créé une petite fonction app() en bas du script. Le petit plus : un booléen en paramètre qui permet d'utiliser le script en mode debug, juste pour voir quelle adresse il va nous retourner au lieu d'effectuer une redirection HTTP.

function app($debug = false) { $servers = loadServers(); $formats = getFormats($servers); $format = (isset($_GET['format']) && array_search($_GET['format'], $formats)) ? $_GET['format'] : getDefaultFormat($servers); $redirect = getServerAffected($servers, $format);

 if ($debug) {

var_dump($redirect); } else { header('Location: '.$redirect); } }

app(true);

2. Configuration des serveurs

Un simple fichier de configuration (mais efficace) en JSON suffira à déclarer les différents serveurs à interroger. J'ai fourni un fichier exemple sur le dépôt GitHub.

{ "servers": { "main": { "host": "127.0.0.1", "port": 8000, "format": "high" }, "relay": { "host": "192.168.0.1", "port": 8000, "format": "high" } }, "default": "main" }

Détaillons un peu le fichier. Tout d'abord, on a une liste de serveurs déclarés avec un petit nom qui nous servira de repère, notamment pour déclarer le serveur par défaut (la dernière valeur du fichier). Chaque serveur est composé de trois paramètres : un host, un port, et un format. Je ne détaillerai pas le host et le port. En revanche, je vais m'arrêter un peu sur le format. Cette variable n'a rien à voir avec une configuration Shoutcast, en réalité, vous pouvez mettre ce que vous voulez dans ce champ : high, medium, low - 256, 128, 96 - génial, moyen, pas terrible, carrément nul.

En fait, le paramètre format permettra de catégoriser automatiquement les différents serveurs déclarés. Cela vous servira avant tout pour proposer différentes URL aux auditeurs, afin qu'ils choisissent la qualité d'écoute qu'ils souhaitent (pratique pour ceux qui disposent d'un débit limité, ou ceux qui doivent compter les GB de données qu'ils consomment dans leur forfait mobile) : cela reste plus pertinent que de proposer plusieurs URLs d'écoute de but en blanc dans un M3U pour choisir un serveur d'écoute. Pour pouvoir utiliser un format précis, il faudra ajouter le paramètre format dans l'URL. Par exemple :

http://ecouter.maradio.com/?format=high

Le filtrage sera fait automatiquement grâce à ce paramètre. Si aucun format n'est fourni en paramètre, le format du serveur par défaut sera pris en compte.

3. Interrogation d'un serveur

L'interrogation du serveur se fait en utilisant fsockopen. J'ai bien tenté d'utiliser cURL, mais pour une raison que je n'explique pas (je n'ai pas cherché à comprendre par manque de temps), cela n'avait pas fonctionné.

/ Read Shoutcast data / function readShoutcastData($host, $port) { $fp=@fsockopen($host, $port); if ($fp) { fputs($fp,"GET /7 HTTP/1.1\nContent-type: text/html; charset: iso-8859-1\nUser-Agent:Mozilla\n\n"); for ($i = 0; $i < 1; $i++) { if (feof($fp)) break; $fp_data = fread($fp, 31337); usleep(500000); } $fp_data = iconv('iso-8859-1', 'utf-8', $fp_data); preg_match("/

(.*)<\/body>/", $fp_data, $fp_data); return $fp_data[1]; } else return ""; }

On va donc faire une requête HTTP bête et méchante vers le serveur de notre choix. Là où les développeurs du serveur Shoutcast ont été très gentil, c'est qu'ils ont fourni une URL très pratique : http://host:port/7 qui va retourner un résultat proche de celui-ci :

9,1,103,10000,8,48,10 The Alan Parsons Project - Eye in the Sky

Pourquoi /7 ? Je ne sais pas… Sans doûte parce que sur cette page, 7 valeurs son retournées (séparées par des virgules).

/ Return number of listeners on server / function getListenersFrom($host, $port) { $data = readShoutcastData($host, $port); list($current, $status, $peak, $max, $reported, $bit, $song) = explode(',', $data, 7); return [$current, $max]; }

Parmi ces 7 valeurs, on retrouve, dans l'ordre :

  • Le nombre d'auditeurs qui écoutent au moment de la requête
  • Un code d'état du serveur (pour indiquer si de la musique tourne ou non)
  • Le pic d'auditeur sur une durée donnée (que je ne connais pas)
  • Le nombre d'auditeur maximum autorisé sur le serveur
  • Le nombre d'auditeurs uniques
  • Le bitrate du flux
  • Les tags de la musique (Artiste - Titre)

Attention pour les tags ! Les artistes et les titres peuvent, eux-aussi, contenir des virgules. On se méfie donc quand on explose la chaîne de caractères : on fixe le maximum d'items à parser à 7, d'où le troisième paramètre dans la fonction explode. Mais cette remarque vous sera utile, surtout pour afficher les tags sur votre site.

4. Sélection du serveur

Une fois que nous avons récupéré le nombre d'auditeurs sur les serveurs qui nous intéressent (en fonction du format désiré), il faut en choisir un. Et là, PHP nous facilite la tâche : array_keys($listeners, min($listeners));.

Comme on a enregistré un tableau dont les clefs sont les URLs des serveurs, et les valeurs, leur nombre d'auditeurs respectifs, il suffit de retourner un tableau de clefs, dont les valeurs sont les plus petites. Rappelons notre objectif principal : le load-balancing. On va donc fournir à notre auditeur qui débarque, le serveur le moins chargé. Comme on a le risque d'avoir deux serveurs avec le même nombre d'auditeurs en sortie, et que de toute façon, la fonction array_keys() retourne un tableau, on prendra soin de récupérer une valeur du tableau (la première par exemple), au lieu de faire un header('Location: …') sur tout un tableau, ce qui n'aurait absolument aucun sens. Et si vraiment notre tableau en sortie était vide, dans ce cas, on retourne le serveur par défaut (cela va de soi : assurez-vous que le serveur par défaut soit stable et suffisamment disponible).

Quand on a l'URL de notre serveur, il ne nous reste plus qu'à la retourner à notre fonction principale pour qu'elle fasse la redirection (ou qu'elle l'affiche dans le cas d'un debug). Arrêtons-nous rapidement sur l'URL d'écoute qui est de la forme suivante :

http://host:port/;

Le point-virgule va être compris par le serveur Shoutcast comme une demande de flux MP3 direct. Si vous tapez cette URL dans votre navigateur, soit il vous affichera un player et vous fera écouter votre radio, soit il vous téléchargera un fichier MP3 sans fin… Le problème c'est que dans beaucoup de lecteurs audios classiques comme Windows Media Player, le point-virgule pose problème, sans vraiment que l'on ne sache pourquoi. Avant d'appeler une belle URL comme http://ecouter.maradio.com/, on pouvait donc ne pas pouvoir écouter notre radio à cause d'un faux problème d'URL. Cependant, comme les lecteurs comprennent, a priori, tous, la redirection HTTP, le problème a été résolu !

--

Résumons

Le script va donc charger en mémoire la liste des serveurs qui diffusent le flux radio (à vous, bien sûr, de proposer des flux qui redirigent sur la même station, ça va de soi !). Ensuite, il va interroger les différents serveurs Shoutcast répondant tous au même format demandé, et va sélectionner celui qui possède le moins d'auditeurs, avant d'effectuer une redirection 302.

L'intérêt est multiple :

  • Nous disposons d'un frontal très simple à mettre en place (installation PHP de base) qui va rediriger tous vos auditeurs vers les serveurs les plus appropriés.
  • Nous pouvons proposer aux auditeurs de saisir des URLs sexy (et surtout simples avant tout, comme http://ecouter.maradio.com/) dans leur player favori
  • On se débarasse des problèmes de compatibilité liés aux URLs avec des caractères un peu spéciaux
  • La configuration est très simple a mettre en œuvre
  • L'auditeur pourra choisir un flux adapté à son support
  • Vous n'aurez plus qu'une seule URL à mettre dans votre M3U (ou éventuellement plusieurs si vous avez besoin de proposer plusieurs formats d'écoute, la meilleure solution restant de proposer un fichier M3U par format d'écoute).

… et tout cela, sans devoir comprendre le système de load-balancing !

Bien entendu, je suis ouvert à toutes corrections et suggestions pour améliorer ce petit script, qui m'aura rendu de fiers services !

Voir ce projet sur GitHub

Petite note : cette solution est adaptée aux petites webradios qui voudraient mettre un répartisseur de charge sur leurs serveurs. Après un conseil que l'on m'a donné, le mieux serait de gérer la charge sans devoir interroger le serveur, directement au niveau du serveur HTTP donc.

Rédigé le .

Commentaires

comments powered by Disqus