Monitoramento e controle de conexões em servidores DataSnap
A versão do DataSnap que acompanha o Delphi XE2 finalmente trouxe o tão esperado e solicitado recurso para controlar e monitorar conexões efetuadas através de TCP/IP em um servidor DataSnap, até a versão XE o monitoramento e controle era possível, entretanto requeria muito código por parte do desenvolvedor. No Delphi XE2 o framework do DataSnap traz classes e métodos que facilitam e muito isso e neste artigo vou explicar como funciona.
Pra começar, toda a implementação do monitoramento e controle está implementada para protocolo TCP/IP o que permite monitorar e derrubar qualquer conexão, assim sendo o componente TDSTCPServerTransporter recebeu dois novos eventos OnConnect e OnDisconnect, estes eventos são muito importantes, pois através deles gerenciamos a lista de conexões no servidor podendo fazer o que quiser com as mesmas. Além disso toda conexão está associado a um Session ID que é único, o Session ID já está disponível desde as versões anteriores do DataSnap.
Para exemplificar o funcionamento e implementação do monitoramento definimos uma lista (collection) conforme abaixo para armazenar as instâncias de cada conexão (TIdTCPConnection) e protocolo (TDSTCPChannel)
FConnections: TObjectDictionary<TIdTCPConnection,TDSTCPChannel>;
A partir dai implementamos os eventos OnConnect e OnDisconnect no TDSTCPServerTransporter. Implementar o evento OnConnect é bem simples, você tem apenas que armazenar as informações referente a conexão na sua lista e caso tenha alguma interface para atualizar com as informações referentes a esta conexão, assim faze-lo.
O código abaixo adiciona a nova conexão utilizando os métodos TMonitor.Enter e TMonitor.Exit para assegurar que nenhuma outra thread irá tentar atualizar esta lista.
Após adicionar a lista utilizamos o método EnableKeepAlive que irei explicar em seguida.
procedure TCMServerForm.CMServerTransportConnectEvent(Event: TDSTCPConnectEventObject); begin System.TMonitor.Enter(FConnections); try FConnections.Add(TIdTCPConnection(Event.Connection), Event.Channel); finally System.TMonitor.Exit(FConnections); end; //Enable Keep-alive for this new connection. Event.Channel.EnableKeepAlive(10000); end;
A implementação do método OnDisconnect terá que localizar a instância da conexão e remove-la da lista, OnDisconnect será executado quando a aplicação cliente desconectar do servidor, seja de forma natural, queda de rede, etc.
Por padrão, o evento OnDisconnect não será notificado se o cliente de forma abrupta perde sua conexão internet. Isso acontece porque a conexão Socket permanece aberta até que ocorra alguma chamada e esta falhe. Se o sistema operacional está configurado para usar pacotes keep-alive para todas as conexões TCP/IP e com base na sua configuração, você verá o evento OnDisconnect ser notificado. Se você desejar controlar este comportamento para cada conexão, então use os métodos EnableKeepAlive e DisableKeepAlive para cada instância do TDSTCPChannel.
No código acima foi definido para cada conexão o KeepAlive em 10 segundos, isso quer dizer que o server irá enviar um pacote (fazer um ping) no cliente quando o mesmo estiver inativo por mais de 10 segundos, se o cliente não responder o server irá tentar novamente, a quantidade de tentativas é baseada no sistema operacional, por exemplo Windows 7 tenta 10 vezes. Você ainda pode definir o intervalo entre os pings, basta configurar o KeepAliveInterval.
O código abaixo demonstra como remover a instância da conexão da lista e como atualizar a interface do usuário utilizando TThread.Synchronize.
procedure TCMServerForm.CMServerTransportDisconnectEvent(Event: TDSTCPDisconnectEventObject); var Index: Integer; begin if (FConnections = nil) or (Event.Connection = nil) then Exit; //This is called when a TCP connection is closed, for whatever reason. System.TMonitor.Enter(FConnections); try FConnections.Remove(TIdTCPConnection(Event.Connection)); TThread.Synchronize(nil, procedure begin //update the connection list box, removing the connection that was just closed Index := ConnectionsList.Items.IndexOfObject(Event.Connection); if Index > -1 then begin ConnectionsList.Items.Delete(Index); if ConnectionsList.SelCount = 0 then SessionIdList.ClearSelection; end; end); finally System.TMonitor.Exit(FConnections); end;
Até aqui sabemos quem está conectado ao servidor, a partir dai podemos derrubar qualquer conexão, visto que temos a instância de cada TDSTCPChannel e este contém o método close que encerra a conexão cliente, ao ser desconectado o lado cliente recebe uma exception quando tentar executar qualquer operação no servidor.
No exemplo abaixo encerramos a conexão selecionada em um ListBox, o método GetSelectedChannel busca a instância do Channel baseado na conexão selecionada no ListBox.
Quando executamos o método Channel.Close o evento OnDisconnect será notificado.
procedure TCMServerForm.ButtCloseSelectedClick(Sender: TObject); var Channel: TDSTCPChannel; begin //Find the selected connection's channel, and then close it. Channel := GetSelectedChannel; //after calling close, the CMServerTransportDisconnectEvent event will be notified. if Channel <> nil then begin Channel.Close; end; end; function TCMServerForm.GetSelectedChannel(Conn: TIdTCPConnection): TDSTCPChannel; begin Result := nil; //Finds the selected connection if Conn = nil then Conn := GetSelectedConnection; //uses the selected connection to look up the associated channel. if Conn <> nil then FConnections.TryGetValue(Conn, Result) end;
O Delphi XE2 traz um exemplo completo sobre o assunto e se você já usa a versão XE2 pode encontrá-lo em C:\Users\Public\Documents\RAD Studio\9.0\Samples\Delphi\DataSnap\DataSnapMonitor ou fazer o download direto do repositório de demos do RAD Studio XE2 aqui.
Olá Lanusse tudo bom..
Primeiramente gostaria de parabeniza-lo pelo blog e pelos artigos que tem publicado são muito bons mesmo, tenho acompanhado seus post´s e estes tem me ajudado muito em minhas duvidas sobre DataSnap e outros assuntos, estou aqui com mais uma duvida que ainda não ficou claro para mim e gostaria que se possível me ajudasse a esclarecer e identificar uma maneira de fazer o controle de transações entre um Cliente e um Servidor DataSnap este assunto e bem explorado nos blog e sites na internet mas nenhum que eu encontrei estava relacionado a uma aplicação DataSnap, desde já agradeço-lhe.
Sou desenvolvedora Delphi e atualmente estamos migrando nossos aplicativos para a estrutura DataSnap. Tenho acompanhado seus posts e outras fontes inclusive as da edn da embarcadero. Mas estou com um problema relativo a: Starto o server, Starto o cliente e tudo funciona normalmente… depois de umas 8 horas assim… geralmente não se reestarta nem o server, nem o client….. o server trava do nada.
Assim sendo, penso que seja algo como Monitoramento e controle de conexões em servidores datasnap… Muito bom o seu post… Só que a nossa versão é a XE… Vc tem algum referencial bom para isso?
Obrigada pela atenção.
Olá Leandra tudo bem, estou passando pelo mesmo problema que você, utilizo o Delphi XE6 com firedac para acesso ao banco de dados firebird, e minhas conexões caem quando muito usuários se conectam, entenda muito como 10, o que na minha concepção é bem pouco, tenho transações que acontecem a todo tempo, troca de informações full time, você conseguiu resolver seu problema? Já criei rotina que “derruba” o server e reinicia e nada. PS eu uso o meu server numa aplicação que roda pelo o IIS, você faz isto também?
Obrigado e aguardo retorno.
Gostaria de saber como faço isto no delphi 2010
olá, poderia por favor me dizer de qual unit fazer parte este cara “ConnectionsList” ??
Desde já agradeço a atenção
Raphael Comba
Sinceramente, eu não consegui usar essa função… É preciso declarar alguma uses ou algo do tipo???
Tiago vc conseguiu, resolver. estou com o mesmo problema que vc!