Como encontrar uma mensagem na fila do MSMQ?

.NET

31/05/2017

Cenário:
Aqui na empresa temos 20 PDV's e todos fazem comunicação com SAT, porém não temos 1 SAT para cada PDV, então foi desenvolvida uma aplicação que distribui as chamadas dos PDV's para os equipamentos SAT disponíveis na loja, uma espécie de "roteador". Quando um PDV fecha a venda e manda os dados para o SAT, as chamadas são enfileiradas no MSMQ, que serão lidas por uma aplicação consumidora dessas mensagens e no final serão colocadas em uma outra fila de "mensagens já processadas", onde cada PDV fará a leitura dos registros e quando encontrar a mensagem que corresponde ao seu pedido original, tira a mensagem da fila de "mensagens já processadas".

Problema:
Acho muito demorado essa última parte, pois o PDV tem que ler a fila inteira pra achar o que precisa, só que se a fila for muito grande, pode haver muita demora para o cliente pagante.

Pergunta:
Será que o vocês poderiam me aconselhar em como localizar a mensagem que o PDV precisa de maneira rápida?

Segue o exemplo de como eu faço a busca.

            MessageQueueTransaction transaction = new MessageQueueTransaction();

            try
            {
                transaction.Begin();

                //As mensagens da fila estão identificadas por uma label e eu uso essa informação para buscar a mensagem na fila
                string label = CreateLabelForMessage(intLojaID, intSatVendaId); 

                Message message = _messageQueue.GetAllMessages().AsParallel().FirstOrDefault(x => x.Label == label);

                if (message == null)
                    return;

                _messageQueue.ReceiveById(message.Id, transaction);

                transaction.Commit();

                SAT_VendaDO objSatVenda = (SAT_VendaDO)message.Body;

                dtoSatVenda.Codigo_Retorno = objSatVenda.Codigo_Retorno;
                dtoSatVenda.Retorno_Motivo = objSatVenda.Retorno_Motivo;
                dtoSatVenda.XML = objSatVenda.XML;
                dtoSatVenda.Data_Retorno = objSatVenda.Data_Retorno;
                dtoSatVenda.Chave_Consulta = objSatVenda.Chave_Consulta;
                dtoSatVenda.Numero_Documento = objSatVenda.Numero_Documento;
                dtoSatVenda.Enum_Status_ID = objSatVenda.Enum_Status_ID;
                dtoSatVenda.SAT_ID = objSatVenda.SAT_ID;
                dtoSatVenda.SAT_Venda_ID = objSatVenda.SAT_Venda_ID;
            }
            catch (Exception ex)
            {
                transaction.Abort();
            }
            finally
            {
                _messageQueue.Close();
            }
            
            return;


Fazendo uma simulação de 20 PDV's acessando, ele retorna a mensagem em torno de 16s, o que acaba sendo muita coisa. Em alguns momentos a consulta que usa Parallel dá a seguinte exception: "A mensagem para a qual o cursor está apontando atualmente foi removida da fila por outro processo ou por outra chamada a Receive sem o uso desse cursor."
Jefferson Cruz

Jefferson Cruz

Curtidas 0

Melhor post

Henrique Gasparotto

Henrique Gasparotto

08/06/2017

Opa Jefferson, vamos lá. Primeiro, o problema de utilizar o paralelismo nessa consulta é justamente o erro que tu tens recebido eventualmente. Conforme o número de buscas paralelas aumenta, esses erros também irão. Sem contar que podem existir inserções enquanto há uma busca, o que complica ainda mais as coisas.

Sobre a implementação, me parece uma questão de trocar a estrutura de dados, abandonando o MSMQ. Como o grande gargalo é para o cliente, trocar a fila por um estrutura indexada é o caminho. Acho que um dicionário de dados seria o ideal, utilizando como "key" um identificador único que os PDV's conhecem. Assim, a busca teria complexidade de O(1). Claro que levaria mais tempo para armazenamento, sem contar a implementação do dicionário, mas no medio/longo prazo tu terias ganho, e nem sei se isso seria possível na tua estrutura.

O paralelismo também é um caminho, mas a natureza do sistema dificulta a implementação correta, e o ganho certamente não seria tão grande.
GOSTEI 1

Mais Respostas

Jefferson Cruz

Jefferson Cruz

31/05/2017

Fiz alguns ajustes no serviço e toda vez que uma mensagem é processada eu guardo ela no MSMQ e também na memória, assim a consulta passa a ser na memória pesquisando dentro de um dicionário pelo numero do pedido, como você citou. Consegui diminuir o tempo de pesquisa de 16s para até 5s.
GOSTEI 0
POSTAR