Jump to content

PoolCounter

From mediawiki.org
This page is a translated version of the page PoolCounter and the translation is 97% complete.
Outdated translations are marked like this.

PoolCounter est un service gestionnaire de verrous écrit en C. PoolCounter implémente une fonctionnalité simulaire au mutex, avec une longueur de file d'attente limitée. Si trop de serveurs essaient de faire la même chose au même moment, la file d'attente se remplit et l'application cliente peut implémenter une action de repli à la place, comme renvoyer une entrée de cache bloqué ou afficher un message d'erreur.

PoolCounter a été créé pour MediaWiki afin d'empêcher le blocage du site suite à une utilisation excessive du CPU après que le cache d'un article populaire ait été invalidé (le cas Michael Jackson). Les sémantiques du sémaphore de PoolCounter permettent à MediaWiki de restreindre le nombre de serveurs web qui analysent simultanément une même nouvelle version d'un article après sa modification. PoolCounter a depuis été utilisé également pour d'autres buts comme pour limiter le taux des demandes de redimensionnement des vignettes.

Installation

Serveur

apt install poolcounter.

Voir packages.debian.org et packages.ubuntu.com. Vous pouvez aussi compiler le serveur à partir de ses sources dans le répertoire mediawiki/services/poolcounter, ou utiliser un serveur Redis standard à la place.

Client

MediaWiki utilise PoolCounter via le client écrit en PHP, configuré via $wgPoolCounterConf , qui prend en charge actuellement deux types de serveurs :

  • PoolCounter_Client – s'appuie sur un serveur Poolcounter (utilisé par Wikipedia).
  • PoolCounterRedis – s'appuie sur un serveur Redis

Le source du client PHP se trouve dans le répertoire /includes/PoolCounter du noyau MediaWiki.

Il existe aussi un client Python expérimental pour le serveur Poolcounter, utilisé par Thumbor.

Configurer

Pour activer le client PoolCounter, activez-le via $wgPoolCounterConf puis spécifiez l'adresse de votre serveur via $wgPoolCountClientConf .

Le client MediaWiki peut spécifier dynamiquement la taille du pool et attendre l'expiration des temporisations utilisées par le serveur PoolCounter. Le serveur PoolCounter en lui-même ne nécessite pas de configuration.

Architecture

Le serveur est un programme C mono thread basé sur libevent. Il n'utilise pas autoconf, il n'a qu'un makefile suffisant pour un environnement Linux standard. Actuellement il n'a pas de code dans un démon et il s'exécute en arrière plan par systemd.

Dans MediaWiki, le client doit être une sous-classe de PoolCounter et la classe qui détient la logique spécifique de l'application doit être une sous-classe de PoolCounterWork. Voir l'utilisation de $wgPoolCounterConf pour les détails.

Protocole

Le protocole réseau est basé sur les lignes, avec des paramètres séparés par des espaces (les espaces dans les paramètres sont codés à l'aide de pourcentages). Le client ouvre une connexion, envoie une commande d'acquisition de verrou, fait le travail, envoie une commande de libération de verrou et ferme la connexion.

Commandes

Les commandes suivantes sont définies :

ACQ4ANY <key> <active worker limit> <total worker limit> <timeout>
Acquérir un verrou pour un client qui a besoin de faire un certain travail, et qui est capable de lire l'entrée du cache écrite par un autre processus. Si on dépasse la limite des activités dans le groupe, le serveur va garder la connexion et retarder la réponse à cette commande. Lorsqu'un client qui a reçu une réponse LOCKED a terminé son travail et a envoyé une commande RELEASE, tous les processus qui attendent avec ACQ4ANY seront immédiatement réveillés par une réponse DONE et peuvent lire le travail réalisé à partir de la nouvelle entrée du cache.
ACQ4ME <key> <active worker limit> <total worker limit> <timeout>
Obtenir un verrou pour les cas où le partage des résultats du travail via un cache n'est pas possible ou non applicable, par exemple lorsqu'une demande de rendu d'un article implique un stub threshold qui n'est pas par défaut. Lorsqu'un verrou de ce type est relâché, un seul processus en attente sera réveillé pour garder identique le nombre des activités en cours.
RELEASE
Relâcher le verrou que le client vient d'acquérir récemment.
STATS [FULL|UPTIME]
imprime les statistiques.

Réponses

Réponses possibles pour ACQ4ANY / ACQ4ME :

LOCKED
acquérir un verrou avec succès. Le client est supposé faire le travail puis envoyer un RELEASE.
DONE
envoyer pour réveiller un client en attente
QUEUE_FULL
il y a plus d'activités que la <limite du nombre total d'activités>
TIMEOUT
il y a plus d'activités que la <limite du nombre d'activités en cours>; aucune place ne s'est dégagée après avoir attendu <timeout> secondes
LOCK_HELD
tentative pour obtenir un verrou quand on en possède déja un

Pour RELEASE :

NOT_LOCKED
actuellement le client ne possède aucun verrou
RELEASED
le verrou a été relâché avec succès

Pour toute commande :

ERROR <message>

Tests

$ echo 'STATS FULL' | nc -w1 localhost 7531 
uptime: 633 days, 15209h 42m 26s
total processing time: 85809 days 2059430h 0m 24.000000s
average processing time: 0.957994s
gained time: 1867 days 44820h 50m 24.000000s
waiting time: 390 days 9365h 18m 24.000000s
waiting time for me: 389 days 9343h 3m 28.000000s
waiting time for anyone: 22h 14m 53.898438s
waiting time for good: 520 days 12503h 48m 24.000000s
wasted timeout time: 473 days 11375h 2m 44.000000s
total_acquired: 7739031655
total_releases: 7736374042
hashtable_entries: 119
processing_workers: 119
waiting_workers: 216
connect_errors: 0
failed_sends: 1
full_queues: 10294544
lock_mismatch: 227
release_mismatch: 0
processed_count: 7739031536

Demander la trace en production

Contrôler rapidement le trafic en production

Sur un serveur d'applications Mediawiki vous pouvez faire :

sudo tcpdump -A 'port 7531 and tcp[tcpflags] & tcp-push != 0'

Support évident de Trivial pour le protocole

Le script Lua suivant est un analyseur basique pour Wireshark qui place uniquement le contenu des paquets réseau Poolcounter dans des chaînes affichables dans une colonne de l'interface utilisateur Wireshark :

--[[
Poolcounter de base contient un analyseur de protocole.
Fournit le contenu du message dans un champ chaîne
qui peut être représenté par une colonne.
Simply renders payload as a string field, which can be then enabled as a column.

Copyright © 2020 Chris Danis & the Wikimedia Foundation

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
--]]

poolcounter_protocol = Proto("PoolCounter", "PoolCounter wire protocol")

pc_command_str = ProtoField.string("poolcounter.cmd")

poolcounter_protocol.fields = {pc_command_str}

function poolcounter_protocol.dissector(buffer, pinfo, tree)
    length = buffer:len()
    if length == 0 then return end
    pinfo.cols.protocol = poolcounter_protocol.name
    local subtree = tree:add(poolcounter_protocol, buffer(), "PoolCounter protocol data")
    subtree:add(pc_command_str, buffer(0,length-1))
end

local tcp_port = DissectorTable.get("tcp.port")
tcp_port:add(7531, poolcounter_protocol)

Sur les systèmes Linux modernes vous devriez pouvoir enregistrer ceci en tant que ~/.local/lib/wireshark/plugins/poolcounter.lua et ensuite il fonctionnera automatiquement dans tout wireshark or tshark.

Tracer l'exécution de certaines formes de requêtes

Imaginez que vous vous intéressiez à voir le flux complet des conversations entre PoolCounter et ses clients pour une certaine partie de l'espace clé -- dans notre exemple nous utiliserons enwiki:SpecialContributions:a:127.0.0.1.

Comme les réponses du serveur PoolCounter (par exemple LOCKED) ne comprennent pas la clé dont ils parlent, ce n'est pas évident à faire.

Commencer la capture des paquets à partir de l'intervalle de temps qui vous intéresse.

Vous pouvez générer cela sur un Poolcounter hôte (ou sur un appserver hôte que vous utilisez pour tester) avec par exemple :

sudo tcpdump tcp port 7531 -c 500000 -w poolcounter.pcap

Ensuite, nous demanderons à Wireshark d'extraire la liste de ses numéros d'identification des flux TCP internes pour toutes les requêtes qui correspondent à cet espace clé :

tshark -r poolcounter.pcap -Y 'poolcounter.cmd contains "enwiki:SpecialContributions:a:127.0.0.1"' -T fields -e tcp.stream | sort | uniq > ids.txt

Une fois la liste des ID obtenue, elle est transformée par un filtre d'affichage Wireshark :

FILTER=$(sed -e 's/^/tcp.stream eq /' -e :a -e 'N;s/\n/ or tcp.stream eq /;ta' ids.txt)

et puis d'utiliser ce filtre pour sélectionner tout le trafic du protocole PoolCounter à partir de ces flux dans la capture des paquets originaux :

tshark -r poolcounter.pcap -Y "poolcounter and ($FILTER)" -e frame.time_relative -e frame.time -e ip.src -e tcp.stream -e poolcounter.cmd -Tfields

Assistance pour le code

Voir aussi