Vérification du portail des services publics régionaux sous charge par le biais de marionnettistes

Bonjour, Habr! Vous aussi, vous êtes curieusement en train de regarder "l'épopée du Far West américain sur la distribution des parcelles de terrain - allez d'abord et collez un drapeau à jalonner" ou peut-être même y participez. Plus précisément dans sa version moderne - soyez le premier à demander des services publics afin de recevoir de l'argent pour les enfants ou obtenir un laissez-passer pour quitter la maison. En regardant tout cela, je voudrais partager l'expérience de notre équipe en testant et en participant à la préparation du portail régional de services pour la fourniture du service "First Class Record". Il est également très similaire à l'effet habra et, je pense, était proche de ce qui s'est passé il y a quelques jours avec le portail fédéral gosuslugi.ru, mais à l'échelle régionale.

Nous avons réussi ce défi à Khabarovsk en janvier de cette année et avons récemment participé à la préparation d'un service similaire pour la délivrance de permis de chasse dans une autre région. Vous trouverez ci-dessous une petite expérience qui vous donnera l'occasion d'examiner la question de la préparation du travail des portails de services publics régionaux pour les périodes de pointe sous un autre angle.

Et pour commencer - une photo d'un bus noir, qui était en service à l'école pendant trois jours jour et nuit, dans lequel l'auteur de ces lignes a confirmé son tour parmi les parents il y a trois ans. Au moins, nos parents se sont réunis. Regarder en hiver à Khabarovsk à -30 degrés est toujours un plaisir.

image

Dans le cadre de la préparation de l'enregistrement de pointe en première année, plusieurs équipes ont travaillé, car d'une manière ou d'une autre y participe: l'exploitant du centre de données, l'exploitant et le développeur du système d'information du portail régional, l'exploitant et le développeur du système intégré d'information pédagogique, l'accompagnement technique des utilisateurs du portail régional. Nous à iondv exécutons la dernière tâche, la surveillance indépendante de la santé des utilisateurs du portail et de soutien.

Notre rôle dans la préparation est d'organiser des tests et des recommandations sur les configurations de mise en cache dans nginx, eh bien, nous avons également préparé des instructions pour les utilisateurs avec le «comportement» recommandé.

Pour ceux qui ne connaissent pas le problème de l'écriture en 1ère année
, . . , , - ( , — ), , - , , . , , , – , . .

1- , . 0:00 , 10:00 26 . , – . 10:00 , — , - .

. . 2017 . . , , . .

Un service pour une ressource demandée comme tâche technique


Le problème dans des services comme «l'écriture au 1er grade» dans l'indicateur intégral de la charge (ou probabilité de requêtes) tendant vers la «fonction delta» (fonction δ, fonction Dirac) est clairement visible sur les graphiques sous forme de pics. En ce moment, il y a une augmentation multiple des appels en peu de temps.

Statistiques des fonctions δ et des requêtes de pointe

D'après notre expérience, la tâche principale de la formation n'est pas d'augmenter les ressources. La tâche consiste à minimiser le nombre potentiel de demandes par seconde, à les étirer pendant une certaine période et à préparer le système pour la charge restante. Dans ce cas, il est nécessaire de trouver et d'accélérer les goulots d'étranglement - cela donnera le plus grand effet conformément aux principes de la théorie des systèmes bornés (principes de Goldratt). Et sinon, c'est le goulot d'étranglement qui échouera. Tout le système devrait fonctionner de lui: le principe du "tambour-corde-tampon".

Il est physiquement impossible de renverser tout le volume en 10 minutes de sablier en 1 minute - il est évident qu'ils s'effondreront. De même pour la prestation de services. Cela ne surprend personne - quand c'est le tour du MFC et les scandales de recevoir des services, mais cela surprend tout le monde - pourquoi le portail est tombé en panne.

Il existe différents modèles de comportement de gestion de charge issus de la théorie de la mise en file d' attente :

  • Vous pouvez mettre les utilisateurs en attente, c'est-à-dire augmenter la file d'attente;
  • vous pouvez simplement refuser de servir ceux qui sont venus plus tard, par exemple, jusqu'à ce que les précédents soient traités;
  • Vous pouvez essayer d'augmenter sans cesse la productivité.

L'opportunité se situe quelque part entre les deux. En effet, pour un service aux ressources limitées fournies par une région ou un État, non seulement la rapidité est importante, mais, tout d'abord, la préservation de la justice sociale - c'est-à-dire des conditions égales pour tous. Dans le même temps - si l'utilisateur n'a pas reçu ce dont il a besoin, il lance une nouvelle demande. Dans ce cas, les demandes se développent dans une avalanche, formant un modèle de l'attaque « effet de pile de chiens » (effet de pile de chiens, ruée du cache, hit miss storm) - l'utilisateur a déjà annulé la demande et en a lancé une nouvelle, tandis que la précédente est toujours dans la file d'attente à traiter.

Ce processus renforce le fait que des familles entières participent à la soumission - papa et maman remplissent les demandes en même temps et soumettent souvent les demandes plusieurs fois pour plus de fiabilité. Et d'ailleurs, souvent aussi dans plusieurs onglets et plusieurs navigateurs. Par conséquent, la charge de pointe attendue a généralement du sens de se multiplier par 2 à 3 fois, à partir du nombre de ceux qui demandent effectivement de tels services.

Retraite de la justice
, «», . ? «» , . . — , . « ». . .

Organisation de la prestation de services


Nous avons calculé le nombre prévu de candidats sur la base d'une combinaison de données sur le nombre total de demandes soumises l'année dernière et de données par minute pour d'autres régions. En règle générale, les applications atteignent un pic à 5-10 minutes, notamment parce que les portails ne répondent presque pas pendant les trois à cinq premières minutes, et les utilisateurs ultérieurs remplissent le formulaire de 1 à 5 minutes (ne soyez pas surpris que beaucoup remplissent même depuis le téléphone dans des conditions «nerveuses») .

Un modèle de calcul approximatif pour les 1000 applications conditionnelles par heure est le suivant:

  • pic de 5 à 10 minutes dès le début et 80% des demandes seront déposées selon la règle de Paretto
  • Classiquement, nous prévoyons 160 applications par minute ou 3 applications par seconde.

En fait, la première soumission s'est produite après une minute et 45 secondes, et le pic des demandes est passé de 4 minutes.

Pour réduire la charge sur ESIA et sur le système de générer des sessions d'autorisation, les instructions suggèrent aux utilisateurs de se connecter à l'avance et d'allonger la durée de vie de la session. En fait, 50% ont été autorisés en 1 heure et ~ 90% en une demi-heure. Nous avons rencontré plus tôt que les utilisateurs ont commencé à se connecter au portail 10 minutes avant le début du service - et l'autorisation a commencé à fonctionner de manière instable. Il est difficile de dire pourquoi. La raison en est peut-être que lorsque le travail technique est effectué la nuit à Moscou, à Khabarovsk, nous n'avons que le début de la journée de travail.

Retraite sur l'enseignement et les dispositions organisationnelles
.

, « » . .. . , - ..

, , , . - . , , .

Il est impossible de supprimer la "fonction delta" lorsque le formulaire est rechargé à 00h00. L'intérêt de cette procédure est que le service apparaît à un moment donné. Mais vous pouvez essayer de réduire le nombre de demandes de navigateur sur toutes les routes utilisateur attendues et ainsi laisser la charge sur le système uniquement à partir des demandes nécessaires - formulaire, répertoires dynamiques et applications d'envoi.

Les paramètres nginx eux-mêmes sont assez standard. Ici, il est plus important de choisir les limitations que le système peut supporter. Ramassez-les - c.-à-d. démarrer les demandes de mise en file d'attente lorsque le serveur devrait atteindre la limite de ses capacités.

Eh bien, et surtout, nous avons forcé la mise en cache (proxy_cache) et augmenté la durée de vie des données "expirées" dans nginx pour tous les chemins statiques et, si possible, les pages dynamiques dans lesquelles il n'y a pas de sessions. Au fait, c'est une erreur courante lors de la mise en cache - en écrivant dans les données de cache (parfois même statiques) dans lesquelles la session de quelqu'un d'autre est enregistrée, la sortie consiste généralement à supprimer ces cookies des en-têtes si le serveur ne peut pas séparer les types de données.

Dans le navigateur de l'utilisateur, cela ressemble à la mise à jour de pages à partir de fichiers téléchargés depuis le disque ou la mémoire. Mais même lorsque l'utilisateur les obtient du serveur, ils sont extraits du cache nginx. Les répertoires eux-mêmes, bien sûr, sont mis en cache dans le système lui-même.

image

Cela a réduit le nombre de demandes potentielles de 89 à 14 et le volume de 2,1 Mo (pour 1000 utilisateurs qui ont mis à jour la page, il s'agit d'un pic potentiel de 4-8 Gbit / s) à 38 Ko (nous nous souvenons tous du webpack, mais pour les plates-formes d'entreprise, ce n'est pas toujours facile à faire). Selon les résultats du passage, il était encore nécessaire de mettre en cache non seulement dans le système, mais aussi dans nginx certains des répertoires du formulaire et des classificateurs dynamiques non utilisés au moment de pointe et de forcer la durée de vie pour eux. Et avec une augmentation de la charge, il est généralement judicieux de mettre la page principale entièrement statique avec les utilisateurs de routage vers le service souhaité ou de créer une ressource distincte pour le service.

Pour réduire la charge d'envoi, les brouillons et le remplissage automatique des données pour l'enfant ont été désactivés. Tous les utilisateurs ont des vitesses de saisie de données différentes, ce qui élimine l'apparence d'un formulaire qui est complètement prêt pour la soumission et évite la fonction delta pour l'envoi des candidatures - les 1000 en une minute. Dans le même temps, la justice sociale est maintenue, bien que, bien sûr, des plaintes apparaissent.

Je ne décrirai pas l'optimisation du système lui-même - lors des tests de charge, des goulots d'étranglement ont été identifiés - principalement dans les requêtes SGBD et les index et les requêtes eux-mêmes ont été optimisés.

L'optimisation la plus importante est probablement de simplifier le formulaire. Qu'est-ce qui affecte le plus la vitesse lorsqu'elle est implémentée dans un formulaire?

  • — , , . — 5-10 ( iPhone ) 5- 375 / (1 10 , application/x-www-form-urlencoded – 20 ), 100 625 /. 100/ — . , « ». — ? , . , ?
  • guides sophistiqués. La charge est généralement augmentée en utilisant le répertoire d'adresses FIAS ou CLADR. Les problèmes ici sont dus à la taille - FIAS prend jusqu'à 40 Go dans la base de données et il faut du temps pour la rechercher. Des dizaines de seconde, mais multipliées par 1000 requêtes simultanées, chargent n'importe quel système. Sans préparation spéciale, peut-être sous la forme d'un service Web distinct et sur une ressource distincte, il est difficile de supporter la charge - par conséquent, ils utilisent souvent un champ de texte brut pour l'adresse.

Eh bien, passons aux tests.

Test de charge en préparation


Les tests ont été effectués par le biais de marionnettistes - en émulant les actions des utilisateurs dans le navigateur Crominium. Yanedeks.tank et JMeter combattent la protection contre les attaques, car ils génèrent un grand nombre de demandes du même type. De plus, ces tests coïncident faiblement avec le profil des requêtes réelles lors de la modification du comportement du système sous charge. De plus, les serveurs mettent en cache les demandes et il est difficile de reproduire une partie des processus en eux (par exemple, l'autorisation). Soit dit en passant, à partir d'un des ateliers devDV , nous avons une présentation avec une présentation sur l'utilisation des marionnettistes pour les tests, y compris charger, lien vers la vidéo .

Pour commencer, nous avons compilé un profil de comportement de l'utilisateur et divisé la procédure en étapes clés:

  1. autorisation de masse en EIES
  2. mise à jour unique du formulaire de service,
  3. alimentation de masse

Pour chacune des étapes, nous avons effectué un test distinct.

L'année dernière, il y avait des difficultés au stade de l'autorisation dans l'EIES, mais le tester à grande échelle est difficile. Le système est externe, la protection contre les attaques et les interdictions d'autorisation est déclenchée. Néanmoins, il est possible de formuler un profil de test pour tester avec précision les goulots d'étranglement du système testé - il s'agit généralement du nombre de sessions simultanément autorisées et des valeurs d'autorisation planifiées par minute, qui peuvent être réglementées par des recommandations.
Dans le test, le wrapper est important pour organiser plusieurs threads, nous utilisons le 'puppeteer-cluster'. Mais généralement, il est plus compliqué de gérer les exceptions et de modifier le comportement du portail sous charge - les éléments de disposition sont souvent révélés qui apparaissent deux fois. Ou les éléments n'apparaissent pas si certaines données ne se sont pas chargées comme prévu. Ce sont toutes les erreurs que les utilisateurs verront et rechargeront la page - ce qui signifie qu'ils créeront une charge supplémentaire. Il existe deux méthodes: implémentez la gestion des exceptions dans le test. Ou modifiez le portail.

Le test lui-même est simple. Ci-dessous est un fragment de cliquer sur le bouton "Connexion" sur le portail de services pour entrer des données dans l'EIES.

await page.waitForSelector(AUTH_AVAIL,{timeout:OPT_ELEM_WAIT_TIME});
const needAuth = await page.$(ELEM_AUTH_IN);
if (!needAuth) throw (new Error(`  `));
        
await page.waitForSelector(AUTH_BUT, OPT_ELEMENT_VISIBLE);
await page.click(AUTH_BUT);
await waitNewUrl(page, 'https://esia.gosuslugi.ru/idp/rlogin?cc=bp', OPT_PAGE_WAIT_TIME);
await page.waitForSelector('#mobileOrEmail', OPT_ELEMENT_VISIBLE);
let text = await elemGetText(page, '#authnFrm > div.login-slils-box > div > div.detected > div.left > div.this-user');
if (text) 
   text = text.replace(/ -\(\)/g, '');        
if (text && text.indexOf(user) === -1) {
  await page.click('div.click-to-another > a');
  await page.waitForSelector('#authnFrm > div.login-slils-box > div >' +
                ' div.detected > div.left > div.this-user', OPT_ELEMENT_INVISIBLE);
}
await page.waitForSelector('#password', OPT_ELEMENT_VISIBLE);
await page.type('#mobileOrEmail', user);
await page.type('#password', pwd);
await page.click('#loginByPwdButton');

Vérification de la mise à jour du formulaire de demande des utilisateurs en attente «ouverture de l'enregistrement». Le test de redémarrage est essentiellement une étape, mais il est important de vérifier les types d'erreurs renvoyées - le réseau est un problème, une erreur nginx, une erreur de serveur et si le formulaire répond aux critères. Et la difficulté est de générer le volume maximum de requêtes en un minimum de temps et de ne pas tomber sous les restrictions de protection (cependant, lors des tests il peut être modifié, d'autre part, c'est aussi une vérification des paramètres d'infrastructure réseau et serveur et WAF).

De tels tests sur les marionnettistes nécessitent beaucoup de ressources pour fonctionner. De facto, il s'est avéré que vous avez besoin d'au moins 2 cœurs contre le 1er cœur du sous-système frontal et d'un canal très large. Mais lorsque vous les louez dans le cloud - c'est assez abordable. Nous avons utilisé Yandex.cloud.

Dans le test, l'autorisation est d'abord mise en œuvre dans l'EIES pour chaque flux séparément. Après cela, un navigateur distinct est lancé pour chaque thread et dans le cadre d'une instance, un nombre donné de mises à jour est effectué. Après cela, l'instance redémarre. La vérification elle-même peut inclure un chemin d'accès typique, par exemple, la page principale, la forme du service. Mais le plus souvent, il suffit de mettre à jour complètement le service et de vérifier dans le répertoire nécessaire que le service peut être soumis - tout comme dans les instructions pour les utilisateurs.

image

Un fragment du test pour ouvrir le principal et rafraîchir la page.

try {
  await page.setViewport(PUP_OPT);
  await page.goto(BASE_URL);
  await page.setCookie(...cookies[worker.id]);
  await page.goto(`${BASE_URL}/nd/lk/form/dnv.htm`);
  rdyRefresh++;
} catch (err) {
  console.error(`#       ${data}: ${err.message}`);
  getErr++;
  await page.screenshot({path: filename});
}
for (let i = 0; i < AMOUNT_REFRESH - 1; i++) {
  const filenameIter = path.join(BASE_DIR, PIC_DIR, `${data}-${i}.png`);
   try {
       await page.reload({waitUntil: ["networkidle0", "domcontentloaded"]});
        rdyRefresh++;
    } catch (err) {
        if (!err.message.includes('Navigation failed because browser')) {
           console.error(`#     ${data}-${i}: ${err.message}`);
           getErr++;
           await page.screenshot({path: filenameIter});
        }
   }
}

Pour le chargement en envoyant des candidatures, l'ensemble du cycle de vérification a été mis en œuvre - avec un rechargement du formulaire et une vérification de la saisie de toutes les données.

Fragment.

for (let i = 0; i < AMOUNT_RESEND; i++) {
   const filename = path.join(BASE_DIR, PIC_DIR, `${data}-${i}.png`);
  try {
     await page.goto('https://uslugi27.ru/nd/lk/form/dnv.htm');
  } catch (err) {
      console.error(`#      1  ${data}-${i}: ${err.message}`);
      await page.screenshot({path: filename});
      getErr++;
      continue;
 }
 try {
     const FORM_PREF = '#createForm > div:nth-child(4) > ';
     await clickDelayed(page,`${FORM_PREF}fieldset.petgroup.ungroupped-attrs > div > div:nth-child(4) > div.col-md-9.attr-data`);
// <…>
     await page.type(`${FORM_PREF}fieldset:nth-child(2) > div > div:nth-child(1) > div.col-md-9.attr-data > input`, '');
// <…>
  } catch (err) {
      console.error(`#      ${data}-${i}: ${err.message}`);
      await page.screenshot({path: filename});
     continue;
  }
  try {
      await page.click('#createForm > div.col_100.controls > button.btn.btn-primary.pull-right.next');
      await clickDelayed(page,`#createForm > div:nth-child(5) > fieldset > div > div:nth-child(1) > div > div`);
       await page.click('#createForm > div:nth-child(5) > fieldset > div > div:nth-child(2) > div > div');
       await page.click('#createForm > div.col_100.controls > button.btn.btn-success.pull-right.submit');
  } catch (err) {
    console.error(`#     ${data}-${i}: ${err.message}`);
    await page.screenshot({path: filename});
    sendErr++;
    continue;
  }

Soit dit en passant, le test peut être accéléré si vous saisissez toutes les données ne provenant pas du marionnettiste par la construction page.type en attente, mais transférez cette logique au navigateur lui-même. Mais alors la complexité des erreurs de capture augmente. Ainsi

document.querySelector('#createForm > div:nth-child(4) > fieldset.petgroup.ungroupped-attrs > div > div:nth-child(4) > div.col-md-9.attr-data').click();
 document.querySelector('#createForm > div:nth-child(4) > fieldset:nth-child(2) > div > div:nth-child(1) > div.col-md-9.attr-data > input').value = '';

Au cours des tests, nous avons fourni plusieurs milliers d'autorisations EIES et environ 16 000 demandes envoyées. Comment a été la restauration d'un système d'information éducatif productif après un tel nombre de déclarations - ne demandez même pas. C'est une histoire complètement différente.

Le principal résultat visible de ce processus a été que les médias locaux s'ennuyaient maintenant à l'époque de l'inscription en première année. Le service a quitté la zone des médias.

En parallèle, nous avons réalisé un tableau de bord pour suivre les performances du formulaire basé sur Grafana: le nombre d'applications, le nombre d'appels, les métriques Yandex, etc. Mais nous laisserons ce sujet pour la prochaine fois.

Eh bien, je voudrais féliciter tous ceux qui sont liés au sujet de l'amélioration de la qualité de la prestation des services publics et municipaux sous forme électronique. Ce travail de préparation sans fin n'a pas été vain - après tout, en avril et en mai, le nombre de candidatures soumises a considérablement augmenté.

All Articles