Oi pessoal! Este post é continuação da série sobre a implementação de um barramento de microservices usando o SQL Server Service Broker (veja a primeira parte aqui).
O primeira passo é montar a infraestrutura no SQL Server. O barramento de microservices é baseado em filas, assim é necessário escolher um banco de dados para a criação das mesmas. Em tese, qualquer banco pode ser usado, mas recomendo a utilização de um banco criado especificamente para esta finalidade, para facilitar as tarefas de infra como backup, monitoração etc.
No nosso caso, criamos um banco novo, chamado, por exemplo, “MyMicroservicesBus”. Após a criação do banco, deve ser feito o comando abaixo, para que o Service Broker seja ativado no mesmo.
ALTER DATABASE MyMicroservicesBus SET ENABLE_BROKER
O próximo passo é criar as filas e contratos. Para quem não é muito familiar com os conceitos do SQL Server Broker, ver referência aqui. Recomendo também o livro “Pro Sql Server 2008 Service Broker”, bem completo. São necessárias duas filas, uma para servir de canal de comunicação da “Thin API” para o Executor (ExecuterQueue) e outra para que ele possa enviar as respostas de volta (InitiatorQueue). Para cada fila é necessário definir um contrato, que vai servir também para o versionamento das mensagens e um Service, para o roteamento de mensagens. Por último, não esquecer de dar as permissões de RECEIVE para os usuários apropriados. Um script completo é mostrado a seguir.
-- messages create message type [http://mydomain.net/services/myservice/requestMessageV1] Validation = None GO create message type [http://mydomain.net/services/myservice/responseMessageV1] Validation = None go -- contracts create contract [http://mydomain.net/services/myservice/contractV1] ( [http://mydomain.net/services/myservice/requestMessageV1] sent by initiator, [http://mydomain.net/services/myservice/responseMessageV1] sent by target ) go -- QUEUES create queue MyServiceInitiatorQueue with status = on go create queue MyServiceExecuterQueue with status = on go -- SERVICES create service MyInitiatorService on queue MyServiceInitiatorQueue ([http://mydomain.net/services/myservice/contractV1]) go create service MyExecuterService on queue MyServiceExecuterQueue ([http://mydomain.net/services/myservice/contractV1]) go -- permissions grant RECEIVE ON MyServiceInitiatorQueue to public go grant RECEIVE ON MyServiceExecuterQueue to public go
Uma vez que as filas estejam definidas, devemos decidir como as mensagens vão ser recebidas peles executores e clientes. Existem várias alternativas para isto, a mais simples seria usar stored procedures para processar mensagens de cada lado. No nosso caso, isto não atende pois temos regras de negócio complexas que devem ser executadas para cada mensagem, que estão implementadas em nosso Domínio e não faria sentido ou seria viável repeti-las em procedures. Assim precisamos de algo que seja capaz de chamar uma regra de negócio dentro do nosso ambiente transacional.
A segunda possibilidade é utilizar o Service Broker External Activator, que é uma ferramenta disponibilizada pela Microsoft para estes cenários. Ele fica “escutando” as filas e se encarrega de chamar um executável externo que, no nosso caso, seria um código em C#. Em nossos testes, funcionou perfeitamente nos nossos ambientes de desenvolvimento e homologação, mas qual a nossa surpresa ao ver que no ambiente de produção ele simplesmente não funcionou. Depois de muito pesquisar, descobrimos que ele não funciona em Clusters de SQL Server, que é exatamente o cenário usado no ambiente de produção do nosso cliente.
A terceira possibilidade, a mais complexa, é utilizar código CLR embutido dentro do SQL Server. Há várias versões do SQL Server é possível compilar uma DLL e registrá-la para uso dentro de um database como se ela fosse uma stored procedure. Esta foi a alternativa escolhida para nosso cenário.
Só recapitulando o procedimento para incluir um código CLR no SQL Server: primeiro, é necessário criar um projeto com uma classe estática e um método estático, que vai ser o ponto de entrada para a chamada no SQL Server, decorado com o atributo Microsoft.SqlServer.Server.StoredProcedure. Depois este assembly deve ser registrado no database e finalmente as queues devem ser modificadas para acionar este método quando uma mensagem chegar. Um exemplo de código CLR e script estão a seguir. Lembrando que caso o assembly não seja assinado com um certificado confiável para o servidor onde está o SQL Server, é necessário baixar o nível de confiança do banco (primeira instrução do script abaixo). Como nossos assemblies não estavam assinados, optamos por fazer isto. Esta decisão deve ser tomada com responsabilidade, pois este comando vai aumentar a vulnerabilidade do banco de dados.
using Microsoft.SqlServer.Server; namespace MyNamespaceInAssembly { public static class StoredProcedures { [SqlProcedure] public static void ProcessMessages() { Domain.Services.ProcessMessages(); } } }
Script:
-- Allows unsigned Assemblies ALTER DATABASE ServiceBroker SET TRUSTWORTHY ON GO -- Register Assemblies CREATE ASSEMBLY MyServiceThinClient from 'c:\myservice\ThinClientProcessor.dll' WITH PERMISSION_SET = UNSAFE go CREATE ASSEMBLY MyServiceExecuter from 'c:\myservice\ExecuterProcessor.dll' WITH PERMISSION_SET = UNSAFE go -- procedures CREATE PROCEDURE ProcessMessagesThinClient AS EXTERNAL NAME MyServiceThinClientAssembly.[MyNamespaceInAssembly.StoredProcedures].ProcessMessages go CREATE PROCEDURE ProcessMessagesExecuter AS EXTERNAL NAME MyServiceExecuterAssembly.[MyNamespaceInAssembly.StoredProcedures].ProcessMessages go -- permissions grant execute on ProcessMessagesThinClient to public go grant execute on ProcessMessagesExecuter to public go -- activations alter queue MyServiceInitiatorQueue with activation( procedure_name = ProcessMessagesThinClient, MAX_QUEUE_READERS = 1, status = on, execute as self) go alter queue MyServiceExecuterQueue with activation( procedure_name = ProcessMessagesExecuter, MAX_QUEUE_READERS = 1, status = on, execute as self) go
Por enquanto é só pessoal. Na parte 3 vou detalhar melhor o código do ProcessMessages, tanto do ThinClient quanto do Executor. Se tiverem dúvidas específicas, podem entrar em contato. Até a próxima!