O Apdex fornece um único número que tenta quantificar a experiência do usuário com requisições a um serviço web. Primeiro, definimos o que consideramos um tempo de resposta aceitável para o nosso serviço; na terminologia do Apdex, isso é chamado de valor T. Em seguida, classificamos as requisições da seguinte forma:
- Todas as respostas de erro são intoleráveis.
- Todas as respostas mais rápidas que T são satisfatórias.
- Todas as respostas mais lentas que T , mas mais rápidas que 4T, são toleráveis.
- Todas as respostas mais lentas que 4T são intoleráveis.
A pontuação Apdex para um serviço, ao longo de um determinado período, é uma proporção de respostas intoleráveis em relação às respostas satisfatórias e toleráveis.
apdex = (satisfatório + (tolerável / 2)) / total
Altas taxas de erro e tempos de resposta lentos resultarão em uma pontuação Apdex mais baixa. Ao encapsular erros e alta latência em uma única métrica, o Apdex tenta quantificar a experiência dos nossos usuários com o nosso serviço em um único número. Como uma pontuação Apdex baixa implica em altas taxas de erros ou latências de resposta indesejáveis, é uma métrica confiável para gerar alertas. Embora uma pontuação Apdex baixa indique problemas de forma confiável, dependendo de onde a medição é feita, uma pontuação alta pode não garantir que tudo esteja bem.
Métricas do aplicativo
Como o Apdex representa a experiência do usuário, o ideal seria medi-lo externamente à aplicação. As métricas do balanceador de carga são uma boa opção ( o Traefik fornece todas as métricas necessárias). O Apdex também pode ser medido diretamente pela aplicação, desde que tenhamos em mente que não há garantia de que veremos todos os erros. Conexões recusadas são um exemplo de erro que provavelmente não é mensurável internamente à aplicação. Por outro lado, a medição no lado da aplicação nos permite maior controle sobre as métricas que produzimos.
Você pode encontrar um exemplo simples de aplicação Go com manipuladores devidamente instrumentados, juntamente com arquivos de configuração do Prometheus, aqui . A instrumentação fornece as seguintes métricas:
- http_apdex_target_seconds é uma métrica de medição que fornece a duração alvo das nossas solicitações.
- http_request_duration_seconds é um histograma dos tempos de resposta com rótulos indicando o nome do manipulador e o código de resposta.
A documentação do Prometheus alerta corretamente contra a adição de muitos rótulos às métricas. Incluir o código de resposta e o manipulador como rótulos é questionável. Os códigos de requisição são arbitrários (embora geralmente variem entre 200 e 599) e incluí-los pode resultar em uma explosão no número de séries temporais que sua aplicação está reportando. Além disso, foi sugerido que diferenciar erros com rótulos pode levar a painéis e alertas deficientes, permitindo que as pessoas se concentrem apenas em chamadas bem-sucedidas. Para o cálculo do Apdex, precisamos diferenciar erros de sucessos. Em nosso exemplo, usamos o código de resposta; outras abordagens são possíveis. Na prática, o autor deste texto não observou problemas ao incluir o código e o manipulador como rótulos em serviços de produção.
Como provavelmente precisaremos do nosso histograma para mais do que apenas cálculos de Apdex, incluímos alguns intervalos adicionais. O único requisito é que tenhamos intervalos exatamente em T e 4T ; outros intervalos são incluídos aqui para demonstrar que não impactam o cálculo geral.
Cálculo do Apdex
A abordagem descrita abaixo utiliza algumas técnicas de consulta Prometheus relativamente avançadas, que espero que você ache interessantes e úteis no futuro. Analisaremos cada regra, explicando a motivação e explicando brevemente como ela funciona. Primeiramente, vamos examinar nossa matéria-prima: quais métricas nossa aplicação está produzindo (filtrei as séries que não precisamos para nossos cálculos).
http_request_apdex_target_seconds 0.1
http_request_duration_seconds_bucket{
code="200",handler="/healthz",le="0.1"} 10
http_request_duration_seconds_bucket{
code="200",handler="/healthz",le="0.4"} 10
http_request_duration_seconds_bucket{
code="200",handler="/healthz",le="+Inf"} 10
http_request_duration_seconds_bucket{
code="200",handler="/q",le="0.1"} 775
http_request_duration_seconds_bucket{
code="200",handler="/q",le="0.4"} 1605
http_request_duration_seconds_bucket{
code="500",handler="/q",le="0.1"} 390
http_request_duration_seconds_bucket{
code="500",handler="/q",le="0.4"} 390
http_request_duration_seconds_count{
code="500",handler="/q"} 390
Como cada instância do aplicativo pode apresentar um valor diferente para qualquer métrica, devemos estabelecer qual é o valor T preferencial atual. Usaremos o valor máximo atual para uma determinada tarefa.
- registro: tarefa:http_request_apdex_target_seconds:max
expr: max(http_request_apdex_target_seconds) POR (tarefa)

Nossa nova métrica é conveniente para adicionar aos gráficos e indicar a latência esperada, mas precisamos ajustá-la para que seja útil para alertas. Os histogramas do Prometheus usam um rótulo ( le ) para identificar cada intervalo. Precisaremos poder selecionar os intervalos com base no nosso valor de T. Poderíamos simplesmente ter gerado algumas métricas com os rótulos necessários, mas, como alternativa, também podemos usar as seguintes regras para gerar as séries necessárias no servidor Prometheus.
- registro: job_le:http_request_apdex_target_seconds:max
expr: |
clamp_max(
count_values(
"le",
job:http_request_apdex_target_seconds:max
) BY (job),
1)
- registro: job_le:http_request_apdex_target_seconds:4_times_max
expr: |
clamp_max(
count_values(
"le",
job:http_request_apdex_target_seconds:max * 4
) BY (job),
1)
Essas regras transformam nossa tarefa selecionada, `job:http_apdex_target_seconds:max`, em duas novas métricas. Elas representam `T` e `4T` . Em vez de simplesmente ter uma métrica com os valores específicos, `count_values` transforma o valor de uma série temporal em um rótulo, `T` em nosso caso , sendo o valor do rótulo uma representação em string dos valores. Como sabemos que temos apenas uma ocorrência dessa métrica para uma determinada instância de nossa aplicação, sabemos que a contagem será sempre 1. Isso resulta nas seguintes métricas.
job_le:http_request_apdex_target_seconds:max{le="0.1"} = 1
job_le:http_request_apdex_target_seconds:4_times_max{le="0.4"} = 1
O rótulo “le” agora pode ser usado para correspondência de rótulos em cálculos que envolvam nosso histograma. Finalmente, podemos calcular nossa pontuação Apdex. Ajustaremos ligeiramente a fórmula.
apdex = (
(
taxaSatisfatória +
(satisfatório + taxaTolerável)
) / 2
) / taxaTotal
Usamos taxas em vez de solicitações totais. Poderíamos ter usado `increase()` em vez de `rate()` , porém o Prometheus estima os aumentos multiplicando as taxas pela duração solicitada, resultando em um valor equivalente. Além disso, como nossos buckets de histograma contabilizam todas as amostras com duração menor ou igual ao bucket específico, o bucket 4T também incluirá amostras do bucket T. Poderíamos subtrair, mas também podemos simplesmente mover a divisão.
Para serviços com baixo tráfego, muitas vezes é desejável ignorar nossas próprias verificações de integridade. As verificações de integridade são úteis para determinar se um serviço é capaz de atender ao tráfego, mas raramente detectam erros inesperados. Frequentemente, elas testam apenas os serviços de back-end de forma trivial, sendo improvável que apresentem timeouts da mesma forma que solicitações de usuários com problemas. Se não ignorarmos nossas verificações de integridade, elas podem aumentar artificialmente nossa pontuação Apdex, ocultando problemas reais em meio a uma névoa de falsos sucessos. Ignoraremos as solicitações ao nosso manipulador de verificações de integridade.
Traduzindo isso para PromQL, obtemos
- registro: job:http_request_apdex:max
expr: |
(
sum(
rate(
http_request_duration_seconds_bucket{
handler!="/healthz",
code!~"5.."}[1m]
)
* ON(job, le) GROUP_LEFT()
job_le:http_request_apdex_target_seconds:max
) BY (job)
+
sum(
rate(
http_request_duration_seconds_bucket{
handler!="/healthz",
code!~"5.."}[1m]
)
* ON(job, le) GROUP_LEFT()
job_le:http_request_apdex_target_seconds:4_times_max
) BY (job)
) / 2
/
sum(
rate(
http_request_duration_seconds_count{
handler!="/healthz"}[1m]
)
) BY (job)
Esta consulta pode ser dividida em seções mais simples. Primeiro, calculamos a taxa de requisições satisfatórias. Conforme as regras da primeira seção, essa taxa é composta por todas as requisições bem-sucedidas (não 5xx) que foram amostradas pelo nosso bucket le=”T” . Em seguida, calculamos as requisições toleráveis do bucket 4T . Finalmente, dividimos a soma dessas requisições por 2 e, em seguida, pelo número total de requisições ao serviço. A seleção do bucket é feita por meio da correspondência de rótulos em nosso rótulo le . As cláusulas GROUP_LEFT são usadas para reter quaisquer rótulos de instrumentação adicionais que estivessem presentes na série temporal original.
Finalmente podemos criar um alerta.
- alerta: HTTPApdexViolation
expr: job:http_request_apdex:max < 0.8
for: 5m
annotations:
description: |
'{{ $labels.job }} has ApDex has fallen below 0.8 {{printf "%.2f" $value}}'
Poderíamos permitir que o limite de alerta, aqui definido como 0,8, fosse especificado pela aplicação, expondo-o como uma métrica, como fizemos para o valor T. No entanto, isso incentivaria os desenvolvedores a diminuir o limite de alerta em resposta a uma aplicação que não esteja atingindo sua meta de Apdex. Na minha opinião, é mais honesto ajustar o tempo de resposta alvo em vez do limite de alerta. Isso incentiva discussões sobre o que é realmente alcançável, em vez de simplesmente manipular um “limite de alerta mágico”.
Conclusão
O Apdex é uma métrica útil para monitoramento, especialmente para alertas. Ele representa de forma concisa a experiência do usuário. O Prometheus nos fornece todas as ferramentas necessárias para calcular o Apdex. A mesma técnica apresentada aqui pode ser usada para estimar métricas semelhantes, focadas no negócio, como orçamentos de erros e disponibilidade do serviço.
FONTE: https://medium.com/@tristan_96324/prometheus-apdex-alerting-d17a065e39d0
