Então esse foi o dia que queimei a lingua!
Uma das criticas que faço às publicações que falam sobre ganhos absurdos de 50%, 80% e até mais de 100% de performance onde todas as variáveis se mantiveram as mesmas, é que afinal: Ganhamos 100% ou deixamos de perder 50%?
Como o velho ditado “A estatística é a arte de torturar os números até que eles confessem o que você quer demonstrar”.
Bem, eu não passei batido à minha própria critica! Hoje vou falar sobre a redução nas alocações de respostas.
O Oragon RabbitMQ é uma implementação de Minimal Api para o consumo de filas do RabbitMQ. Talvez eu pense algum dia em mudar o nome e criar.
Seguindo o AMQP temos 3 tipos de resposta padrão e outras customizadas
- Respostas Padrão
- RejectResult
- NackResult
- AckResult
- Respostas Customizadas
- ReplyResult
- ComposableResult
As respostas padrão RejectResult e NackResult , diferente do AckResult, possuem um boolean no construtor que define se há reenfileiramento ou não.
Revisitando a implementação, me incomodou a necessidade de criar uma instância de RejectResult e NackResult , ou AckResult para cada mensagem processada.
Obviamente a Microsoft possui o mesmo problema com os Results das Minimal API’s, então fui ver como a Microsoft resolve o problema por lá, para entender se existia alguma ideia genial ou se era basicamente um cache mesmo.
Em resumo, nas Minimal API’s (Microsoft.AspNetCore.Http.Results) a classe
Microsoft.AspNetCore.Http.Results
interage com Microsoft.AspNetCore.Http.
TypedResults
que por sua vez usa o cache implementado em Microsoft.AspNetCore.Http.ResultsCache
que por sua vez faz cache em memória de 1 instância para cada status HTTP. De 101, passando por 200, até 511.
Então sempre que você não customiza a resposta, um
StatusCodeHttpResult
, que está em ResultCache é devolvido, já que essa classe implementa tanto IResult
quanto IStatusCodeHttpResult
.
Eu não precisava dessa separação tão rígida em Helper (Results), Factory (TypedResults) e Cache (ResultsCache).
Aqui implementei as 3 responsabilidades em AmqpResults e continuou simples, principalmente pela pouca necessidade de manutenção e ficou assim.
namespace Oragon.RabbitMQ.Consumer.Actions; /// <summary> /// Construct and cache results /// </summary> public static class AmqpResults { private static readonly AckResult s_forSuccess = new(); private static readonly NackResult s_nackWithRequeue = new(true); private static readonly NackResult s_nackWithoutRequeue = new(false); private static readonly RejectResult s_rejectWithRequeue = new(true); private static readonly RejectResult s_rejectWithoutRequeue = new(false); /// <summary> /// Return an AckResult to represents a AMQP Ack /// </summary> /// <returns></returns> public static AckResult Ack() => AmqpResults.s_forSuccess; /// <summary> /// Return an NackResult to represents a AMQP Nack /// </summary> /// <returns></returns> public static NackResult Nack(bool requeue) => requeue ? AmqpResults.s_nackWithRequeue : AmqpResults.s_nackWithoutRequeue; /// <summary> /// Return an RejectResult to represents a AMQP Reject /// </summary> /// <returns></returns> public static RejectResult Reject(bool requeue) => requeue ? AmqpResults.s_rejectWithRequeue : AmqpResults.s_rejectWithoutRequeue; /// <summary> /// Return a ReplyResult to represents a AMQP Reply /// </summary> /// <typeparam name="T"></typeparam> /// <param name="objectToReturn"></param> /// <returns></returns> public static ReplyResult<T> Reply<T>(T objectToReturn) => new(objectToReturn); /// <summary> /// Return a ComposableResult to execute multiple steps /// </summary> /// <param name="results"></param> /// <returns></returns> public static ComposableResult Compose(params IAmqpResult[] results) => new(results); }
Assim segui a mesma estratégia da Microsoft e reduzi drasticamente a criação de novas instâncias para cada mensagem consumida.
Isso já pode ser visto na versão 1.1 que foi disponibilizada na última semana.
Exemplos de Uso
AmqpResults.Ack(); AmqpResults.Nack(requeue: true); AmqpResults.Nack(requeue: false); AmqpResults.Reject(requeue: true); AmqpResults.Reject(requeue: false);
Os demais usos como ReplyResult e ComposableResult em comento em algum espaço apropriado.
0 comentários