|
|
Testes de carga para chatbots implementados com a stack Rasa
|
|
|
=======
|
|
|
|
|
|
O proposito dessa wiki é dimensionar, a partir de cenários de teste de carga executados na api do Rasa, qual a infraestrutura necessária para atender um número específico de requisições simultâneas no chatbot. Nesses testes não levaremos em consideração possíveis conectores (rocketchat, telegram) utilizados para exibir as respostas geradas pelo Rasa, todas as requisições serão feitas no endpoint que o Rasa disponibiliza para tal integração.
|
|
|
|
|
|
# Rasa
|
|
|
|
|
|
A versão do rasa utilizada nos testes de carga é a 1.2.2. Essa versão possui como dependência o projeto Senic, que é utilizado para servir os endpoints de integração com frontends diversos como rocketchat e telegram. O Senic é um servidor web asíncrono e utiliza a arquitetura asyncio do python (introduzida no python 3.4) para a implementação dos endpoints. O rasa possui um endpoint específico para integração com os frontends que é a rota **/webhooks/rest/webhook**. No asyncio cada chamada a um método asíncrono cria um nova thread no sistema operacional. O número de threads que um endpoint do Senic pode criar é definido pela regra: `os.cpu_count() * 5`. Essa regra mudou a partir do python 3.7 (https://bugs.python.org/issue35279).
|
|
|
O ambiente de testes possui 6 cpus, então teremos, para cada instância (Pod) do Senic no máximo trinta threads disponíveis. Isso implica que, em media, para cada instância do Senic teremos a capacidade de processamento de 30 requisições por vez. Para avaliar se essa media condiz com a realidade, iremos utilizar o software JMeter (https://jmeter.apache.org) para executar cenários de teste em que requisições serão disparadas na api do Rasa, e a partir do tempo de resposta e requisições corretamente processadas estimaremos a infraestrutura necessária para diferentes volumes de acesso.
|
|
|
|
|
|
# Testes
|
|
|
# Metodologia
|
|
|
|
|
|
|
|
|
O primeiro teste que iremos fazer irá avaliar nosso ponto de partida, de que uma única instância do rasa consegue processar de maneira asíncrona trinta requisições paralelas. A instância do rasa que iremos utilizar se encontra no domínio http://boilerplate.lappis.rocks/ e está sendo disponibilizada por um cluster no kubernetes, gerenciado pelo rancher. O deploy do rasa foi feito via Helm e pode ser encontrado no [repositório de charts do lappis](https://gitlab.com/lappis-unb/charts).
|
|
|
A instância do rasa que iremos utilizar se encontra no domínio http://boilerplate.lappis.rocks/ e está sendo disponibilizada por um cluster no kubernetes, gerenciado pelo rancher. O deploy do rasa foi feito via Helm e pode ser encontrado no [repositório de charts do lappis](https://gitlab.com/lappis-unb/charts).
|
|
|
|
|
|
Para a criação dos cenários de teste estaremos utilizando o Jmeter na versão 5.1.1. As requisições http enviadas via Jmeter terão o seguinte padrão;
|
|
|
|
... | ... | @@ -27,48 +27,7 @@ As respostas da api variam de acordo com o processamento feito pelo rasa, mas de |
|
|
[{"recipient_id":"default","text":"Foi um prazer te ajudar!\nSempre que precisar, volte aqui!\nAt\u00e9 a pr\u00f3xima!\n"}]
|
|
|
```
|
|
|
|
|
|
O endpoint que processa as requisições enviadas na rota `/webhooks/rest/webhook/` possui a seguinte implementação:
|
|
|
```
|
|
|
@custom_webhook.route("/webhook", methods=["POST"])
|
|
|
async def receive(request: Request):
|
|
|
sender_id = await self._extract_sender(request)
|
|
|
text = self._extract_message(request)
|
|
|
should_use_stream = rasa.utils.endpoints.bool_arg(
|
|
|
request, "stream", default=False
|
|
|
)
|
|
|
input_channel = self._extract_input_channel(request)
|
|
|
|
|
|
if should_use_stream:
|
|
|
return response.stream(
|
|
|
self.stream_response(
|
|
|
on_new_message, text, sender_id, input_channel
|
|
|
),
|
|
|
content_type="text/event-stream",
|
|
|
)
|
|
|
else:
|
|
|
collector = CollectingOutputChannel()
|
|
|
# noinspection PyBroadException
|
|
|
try:
|
|
|
await on_new_message(
|
|
|
UserMessage(
|
|
|
text, collector, sender_id, input_channel=input_channel
|
|
|
)
|
|
|
)
|
|
|
except CancelledError:
|
|
|
logger.error(
|
|
|
"Message handling timed out for "
|
|
|
"user message '{}'.".format(text)
|
|
|
)
|
|
|
except Exception:
|
|
|
logger.exception(
|
|
|
"An exception occured while handling "
|
|
|
"user message '{}'.".format(text)
|
|
|
)
|
|
|
return response.json(collector.messages)
|
|
|
```
|
|
|
|
|
|
Note que nessa implementação o endpoint congela a execução na chamada do método `on_new_message`. É nesse ponto que cada thread vai aguardar o rasa processar o texto enviado pelo usuário. Assim que a mensagem é enviada de volta pelo rasa o endpoint encerra sua execução e a thread é removida. Os cenários de teste a seguir irão disparar requisições http no endpoint de webhook, enviando no body algum texto a ser respondido pelo bot. A meta dos cenários é prover insumos para que recursos de infraestrutura possam ser dimensionados a partir de dois critérios chaves:
|
|
|
|
|
|
Os cenários de teste a seguir irão disparar requisições http no endpoint de webhook, enviando no body algum texto a ser respondido pelo bot. A meta dos cenários é prover insumos para que recursos de infraestrutura possam ser dimensionados a partir de dois critérios chaves:
|
|
|
1. O volume médio de acessos simultâneos esperado.
|
|
|
2. O tempo de resposta que deve ser proximo a três segundos.
|
|
|
|
... | ... | @@ -160,3 +119,11 @@ Nesse cenário teremos dez usuários enviando quatro mensagens cada. Manteremos |
|
|
| porcentagem de respostas abaixo de 3s | porcentagem de respostas acima de 3s |
|
|
|
| ------ | ------ |
|
|
|
| 85% | 15% |
|
|
|
|
|
|
|
|
|
# Análise dos Cenários
|
|
|
|
|
|
Realizando uma conta simples, dez acessos por segundo extrapolados para o dia representam 864000.
|
|
|
|
|
|
|
|
|
|