Verificação do portal de serviços públicos regionais sob carga através do operador de marionetes

Olá Habr! Você também está assistindo curiosamente o "épico do oeste selvagem americano na distribuição de terrenos - vá primeiro e coloque uma bandeira na estaca" ou talvez até participe. Mais precisamente em sua versão moderna - seja o primeiro a solicitar serviços públicos para receber dinheiro para crianças ou obter um passe para sair de casa. Olhando para tudo isso, gostaria de compartilhar a experiência de nossa equipe em testar e participar da preparação de um portal regional de serviços para a prestação do serviço "First Class Record". Também é muito parecido com o efeito habra e, acho, foi próximo ao que aconteceu alguns dias atrás com o portal federal gosuslugi.ru, mas em escala regional.

Superamos esse desafio em Khabarovsk em janeiro deste ano e recentemente participamos da preparação de um serviço semelhante para a emissão de licenças de caça em outra região. Abaixo está uma pequena experiência que lhe dará a oportunidade de analisar a questão da preparação do trabalho dos portais regionais de serviços públicos para períodos de pico de outra perspectiva.

E para iniciantes - uma foto de um ônibus preto, que estava de serviço na escola por três dias, 24 horas por dia, no qual o autor dessas linhas confirmou sua vez entre os pais há três anos. Pelo menos nossos pais se uniram. Assistindo no inverno em Khabarovsk a -30 graus ainda é um prazer.

imagem

No processo de preparação para o pico de gravação na primeira série, várias equipes trabalharam, porque de uma maneira ou de outra: o operador do centro de dados, o operador e o desenvolvedor do sistema de informações do portal regional, o operador e o desenvolvedor do sistema integrado de informações da educação, o suporte técnico aos usuários do portal regional. Na iondv, realizamos a última tarefa, monitorando independentemente a saúde do portal e dando suporte aos usuários.

Nosso papel na preparação é organizar testes e recomendações sobre configurações de armazenamento em cache no nginx. Bem, também preparamos instruções para usuários com o "comportamento" recomendado.

Para quem não conhece o problema da escrita na 1ª série
, . . , , - ( , — ), , - , , . , , , – , . .

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

. . 2017 . . , , . .

Um serviço para um recurso exigido como uma tarefa técnica


O problema em serviços como "escrever para a 1ª série" no indicador integral da carga (ou probabilidade de consultas) tendendo à "função delta" (função δ, função Dirac) é claramente visível nos gráficos na forma de picos. Neste momento, há um aumento múltiplo de chamadas em um curto período de tempo.

função δ e estatísticas de consulta de pico

Nossa experiência diz que a principal tarefa do treinamento não é aumentar recursos. A tarefa é minimizar o número potencial de solicitações por segundo, esticá-las por um período e preparar o sistema para a carga restante. Nesse caso, é necessário encontrar e acelerar gargalos - isso produzirá o maior efeito de acordo com os princípios da teoria dos sistemas limitados (princípios de Goldratt). E, caso contrário, é o gargalo que irá falhar. Todo o sistema deveria funcionar com ele: o princípio da "corda do tambor".

É fisicamente impossível derramar o volume inteiro em 10 minutos de ampulheta em 1 minuto - é óbvio que eles entrarão em colapso. Da mesma forma para a prestação de serviços. Não surpreende ninguém - quando é a vez do MFC e dos escândalos de receber serviços, mas surpreende a todos - por que o portal foi desativado.

Existem diferentes padrões de comportamento de manipulação de carga da teoria das filas :

  • Você pode colocar os usuários em espera, ou seja, aumentar a fila;
  • você pode simplesmente se recusar a atender aqueles que vieram mais tarde, por exemplo, até que os anteriores sejam processados;
  • Você pode tentar aumentar infinitamente a produtividade.

A conveniência está algures no meio. De fato, para um serviço com um recurso limitado fornecido por uma região ou estado, não apenas a velocidade é importante, mas, antes de tudo, a preservação da justiça social - ou seja, igualdade de condições para todos. Ao mesmo tempo - se o usuário não recebeu o que precisa, ele inicia uma nova solicitação. Nesse caso, as solicitações crescem em uma avalanche, formando um modelo de ataque “ efeito de pilha de cachorro (efeito de pilha de cachorro, carimbo de cache, tempestade de acertos) - o usuário já cancelou a solicitação e iniciou uma nova, enquanto a anterior ainda está na fila a ser processada.

Esse processo reforça o fato de famílias inteiras participarem da submissão - pai e mãe preenchem as solicitações ao mesmo tempo e frequentemente enviam solicitações várias vezes por questões de confiabilidade. Além disso, muitas vezes também em várias guias e vários navegadores. Portanto, o pico de carga esperado geralmente faz sentido multiplicar por 2-3 vezes, a partir do número daqueles que realmente solicitam esses serviços.

Retiro da justiça
, «», . ? «» , . . — , . « ». . .

Organização da prestação de serviços


Calculamos o número esperado de candidatos com base em uma combinação de dados sobre o número total de solicitações enviadas no ano passado e dados por minuto para outras regiões. Normalmente, o pico de aplicativos cai em 5 a 10 minutos, inclusive porque os portais quase não respondem nos primeiros três a cinco minutos, e os usuários posteriores preenchem o formulário de 1 a 5 minutos (não se surpreenda, muitos são preenchidos pelo telefone, mesmo em condições "nervosas") .

Um modelo de cálculo aproximado para as 1000 aplicações condicionais por hora é o seguinte:

  • pico de 5 a 10 minutos desde o início e 80% dos pedidos serão apresentados sob a regra de Paretto
  • Convencionalmente, planejamos 160 aplicativos por minuto ou 3 aplicativos por segundo.

De fato, o primeiro envio ocorreu após um minuto e 45 segundos, e o pico de pedidos passou de 4 minutos.

Para reduzir a carga no ESIA e no sistema de gerar sessões de autorização, as instruções sugeriram que os usuários efetuem login com antecedência e prolongem a vida útil da sessão. De fato, 50% foram autorizados em 1 hora e ~ 90% em meia hora. Descobrimos anteriormente que os usuários começaram a fazer login no portal 10 minutos antes do início do serviço - e a autorização começou a funcionar de maneira instável. É difícil dizer o porquê. Talvez a razão seja a seguinte: quando o trabalho técnico é realizado à noite em Moscou, em Khabarovsk, temos apenas o começo do dia útil.

Retiro sobre instruções e arranjos organizacionais
.

, « » . .. . , - ..

, , , . - . , , .

É impossível remover a "função delta" quando o formulário é recarregado às 00:00. O objetivo deste procedimento é que o serviço seja exibido em um determinado momento. Mas você pode tentar reduzir o número de solicitações de navegador em todas as rotas de usuário esperadas e, assim, deixar a carga no sistema apenas das necessárias - formulário, diretórios dinâmicos e aplicativos de envio.

As próprias configurações do nginx são bastante padrão. Aqui é mais importante escolher as limitações que o sistema pode suportar. Pegue-os - ou seja, iniciar solicitações de enfileiramento quando o servidor atingir o limite de seus recursos.

Bem, e o mais importante, forçamos o cache (proxy_cache) e aumentamos a vida útil dos dados "expira" no nginx para todos os caminhos estáticos e, sempre que possível, páginas dinâmicas nas quais não há sessões. A propósito, esse é um erro comum ao armazenar em cache - gravando nos dados do cache (às vezes até na estática) em que a sessão de outra pessoa é salva, a saída geralmente é excluir esses cookies dos cabeçalhos se o servidor não puder separar os tipos de dados.

No navegador do usuário, parece atualizar páginas de arquivos baixados do disco ou da memória. Mas mesmo quando o usuário os obtém do servidor, eles são retirados do cache nginx. Os próprios diretórios, é claro, são armazenados em cache no próprio sistema.

imagem

Isso reduziu o número de solicitações em potencial de 89 solicitações para 14 e o volume de 2,1 MB (para 1000 usuários que atualizaram a página, este é um pico potencial de 4-8 Gbit / s) para 38 Kb (todos lembramos do webpack, mas para plataformas de empresa, isso não é sempre fácil de fazer). De acordo com os resultados da passagem, ainda era necessário armazenar em cache não apenas no sistema, mas também no nginx, alguns dos diretórios do formulário e classificadores dinâmicos não utilizados no momento de pico e forçar a vida útil deles. E com o aumento da carga, geralmente faz sentido colocar a página principal totalmente estática com o roteamento de usuários para o serviço desejado ou criar um recurso separado para o serviço.

Para reduzir a carga no envio, os rascunhos e o preenchimento automático de dados para a criança foram desativados. Todos os usuários têm velocidades diferentes de entrada de dados, o que elimina a aparência de um formulário completamente pronto para envio e evita a função delta para o envio de aplicativos - todos os 1000 em um minuto. Ao mesmo tempo, a justiça social é mantida, embora, é claro, surjam reclamações.

Não descreverei a otimização do próprio sistema - durante o teste de carga, foram identificados gargalos - principalmente nas consultas DBMS e os próprios índices e consultas foram otimizados.

Provavelmente a otimização mais importante é simplificar o formulário. O que afeta mais a velocidade quando implementado em um formulário?

  • — , , . — 5-10 ( iPhone ) 5- 375 / (1 10 , application/x-www-form-urlencoded – 20 ), 100 625 /. 100/ — . , « ». — ? , . , ?
  • guias sofisticados. A carga geralmente é aumentada usando o diretório de endereço FIAS ou CLADR. Os problemas aqui são devidos ao tamanho - o FIAS leva até 40 GB no banco de dados e leva tempo para procurá-lo. Décimos de segundo, mas multiplicados por 1000 solicitações simultâneas, carregam qualquer sistema. Sem preparação especial, possivelmente na forma de um serviço da Web separado e em um recurso separado, é difícil suportar a carga - portanto, eles costumam usar um campo de texto sem formatação para o endereço.

Bem, vamos aos testes.

Teste de carga em preparação


O teste foi realizado através do operador de marionetes - emulando as ações do usuário no navegador Crominium. Yanedeks.tank e JMeter eliminam a proteção contra ataques, porque eles geram muitos dos mesmos tipos de solicitações. Além disso, esses testes coincidem fracamente com o perfil de consultas reais ao alterar o comportamento do sistema sob carga. Além disso, os servidores armazenam em cache solicitações e é difícil reproduzir parte dos processos neles (por exemplo, autorização). A propósito, em um dos seminários do devDV , temos uma apresentação sobre o uso de marionetistas para testes, incluindo carregar, link para o vídeo .

Para começar, compilamos um perfil de comportamento do usuário e dividimos o procedimento em etapas principais:

  1. autorização em massa na ESIA
  2. atualização única do formulário de serviço,
  3. alimentação em massa

Para cada uma das etapas, fizemos um teste separado.

No ano passado, houve dificuldades na fase de autorização na ESIA, mas testá-lo em larga escala é difícil. O sistema é externo, a proteção contra ataques e proibições de autorização é acionada. No entanto, é possível formular um perfil de teste para testar com precisão os gargalos do sistema em teste - geralmente esse é o número de sessões autorizadas simultaneamente e os valores de autorização planejados por minuto, que podem ser regulados por recomendações.
No teste, o wrapper é importante para organizar vários threads, usamos o 'puppeteer-cluster'. Mas geralmente é mais complicado lidar com exceções e alterar o comportamento do portal sob carga - os elementos de layout geralmente são revelados e aparecem duas vezes. Ou os elementos não aparecem se alguns dados não foram carregados conforme o esperado. Esses são todos os erros que os usuários verão e recarregam a página - o que significa que eles criarão uma carga adicional. Existem duas maneiras: implementar o tratamento de exceções no teste. Ou modifique o portal.

O teste em si é simples. Abaixo está um fragmento, clicando no botão "Login" no portal de serviços para inserir dados na ESIA.

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');

Verificação da atualização do formulário de inscrição com usuários pendentes "abrindo o registro". O teste de reinicialização é essencialmente uma etapa, mas é importante verificar os tipos de erros retornados - a rede é um problema, um erro nginx, um erro do servidor e se o formulário atende aos critérios. E a dificuldade é gerar o volume máximo de solicitações no menor tempo possível e não se enquadrar nas restrições de proteção (no entanto, durante os testes, pode ser alterado, por outro lado, é também uma verificação das configurações de infraestrutura de rede e servidor e WAF).

Tais testes em marionetistas exigem muitos recursos para funcionar. De fato, você precisa de pelo menos 2 núcleos contra o primeiro núcleo do subsistema front-end e um canal muito amplo. Mas ao alugá-los na nuvem - isso é bastante acessível. Usamos o Yandex.cloud.

No teste, a autorização é implementada primeiro na ESIA para cada fluxo separadamente. Depois disso, um navegador separado é iniciado para cada encadeamento e, dentro da estrutura de uma instância, um determinado número de atualizações é realizado. Depois disso, a instância reinicia. A verificação em si pode incluir um caminho típico, por exemplo, a página principal, a forma do serviço. Porém, mais frequentemente, basta atualizar completamente o serviço e verificar o diretório necessário para que o serviço possa ser enviado - tudo como nas instruções para os usuários.

imagem

Um fragmento do teste para abrir o principal e atualizar a página.

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});
        }
   }
}

Para a carga enviando aplicativos, todo o ciclo de verificação foi implementado - com uma recarga do formulário e verificação da entrada de todos os dados.

Fragmento.

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;
  }

A propósito, a aprovação no teste pode ser acelerada se você digitar todos os dados que não são do apresentador de marionetes pela construção aguardar page.type, mas transferir essa lógica para o próprio navegador. Mas então a complexidade da captura de erros aumenta. Igual a

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 = '';

Durante os testes, fornecemos vários milhares de autorizações ESIA e cerca de 16 mil aplicativos enviados. Como foi a restauração de um sistema produtivo de informações educacionais após tantas declarações - nem pergunte. Esta é uma história completamente diferente.

O principal resultado visível desse processo foi que a mídia local estava entediada agora nos dias de matrícula na primeira série. O serviço saiu da área de mídia.

Paralelamente, criamos um painel para monitorar o desempenho do formulário com base no Grafana: número de aplicativos, número de chamadas, métricas Yandex etc. Mas deixaremos esse tópico para a próxima vez.

Bem, gostaria de felicitar todos os que estão conectados com o tópico de melhorar a qualidade da prestação de serviços estaduais e municipais em formato eletrônico. Esse trabalho preparatório interminável não foi em vão - afinal, em abril e maio, o número de solicitações apresentadas aumentou significativamente.

All Articles