Arquitetura de Microservices utilizando o Microsoft SQL Service Broker – Parte 2

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!

, , , , ,

  1. Deixe um comentário

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair /  Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair /  Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair /  Alterar )

Conectando a %s

%d blogueiros gostam disto: