Esta série de artigos irá mostrar com usar nosseus softwares o middleware LUCAS-GABRIEL, que tem a finalidade deaumentar a escalabilidade do SGBD através da distribuição horizontal dos dados.
Apresentação
Sistemas que entram em produção tendem a perderdesempenho após um determinado tempo. Algumas vezes essa perda de desempenho estárelacionada com consultas de dados mal elaboradas, mas na maioria das vezes oproblema está relacionado a sobrecarga que os servidores de dados sofrem com oaumento dos dados armazenados.
Na tentativa de resolver este problema aCASSIC está desenvolvendo o middleware LUCAS-GABRIEL. Este middlewareefetua a distribuição horizontal dos dados entre instâncias distintas deSGBDRs, mas sem interferir diretamente sobre o processamento dos SGBDRs.
Figura 1 – Arquitetura do LUCAS-GABRIEL
Esta solução impacta diretamente sobre acomunidade de programadores e softhouses, pois o LUCAS-GABRIEL tem a pretensãode ser independente do banco de dados e da linguagem de programação utilizada,o que diminui significativamente a curva para a interoperabilidade entresistemas feitos em linguagens distintas e banco de dados heterogêneos, cenáriocomum nas empresas da atualidade. Existem outras soluções para este problema,mas diferentemente do LUCAS-GABRIEL, elas são direcionadas para um banco dedados ou linguagem de programação especifica.
Já foram feitos teste iniciais emlaboratório e alguns teste em ambiente de produção controlado, nos dois casosforam obtidos bons rendimentos. Agora estamos colocando ele a disposição dacomunidade para que todos avaliem e dêem feedbacks. A intenção da CASSIC édesenvolver um middleware que seja GRATUITO e CONFIÁVEL.
Com a finalidade de apresentar e divulgar acomunidade, será apresentado uma série de artigos mostrando as funcionalidadesdo LUCAS-GABRIEL em conjunto com o Delphi. Esperamos que apreciem e dêem seusfeedbacks sobre o produto.
Preparando os Bancos de Dados
Foi dito anteriormente que o LUCAS-GABRIEL “tema pretensão de ser independente do banco de dados”, mas para efetuar osprimeiros testes e avaliarmos melhor o desempenho do banco de dados, nestaprimeira versão beta só foi implementada a conexão com o Firebird. Colocadaesta explicação, vamos a construção do banco de dados.
Abaixo seguem os comandos DDLs para acriação das tabelas que serão usadas para mostrar as funcionalidades do LUCAS-GABRIEL.
create table Cliente(
ID integer not null primary key,
Nome varchar(50),
Limite_Credito numeric(15,2)
);
create table Produto(
ID integer not null primary key,
Descricao varchar(50),
Vr_Venda numeric(15,2)
);
create table PDV(
ID integer not null primary key,
Dt_Venda date,
ID_Cliente integer,
foreign key(ID_Cliente) references Cliente(ID)
);
create table PDV_Item(
ID integer not null primary key,
Quant numeric(15,3),
Vr_Unit numeric(15,2),
ID_PDV integer,
ID_Produto integer,
foreign key(ID_PDV) references PDV(ID),
foreign key(ID_Produto) references Produto(ID)
);
Devido o LUCAS-GABRIEL efetua a distribuiçãohorizontal, estes mesmos comandos DDLs devem ser repetidos para cadainstância do SGBD. Se desejar, pode efetuar a cópia de todo o banco de dadospara as máquinas onde os SGBDs estão instanciados. É importante ter em menteque na distribuição horizontal, as mesmas tabelas e colunasexistentes numa instância do SGBD devem existir nas outras instâncias.
Instalação do LUCAS-GABRIEL
Os arquivos necessários para usar oLUCAS-GABRIEL podem ser baixados de
Observação:Osistema que vai ser desenvolvido para testar o LUCAS-GABRIEL será citado desteponto por diante como Sistema Cliente.
Na máquina onde ficará Sistema Clientenão é instalado nenhum driver de conexão com o SGBD, nela deve-secolocar apenas o arquivo “LUCAS.dll” que está dentro do arquivo baixado.
Numa máquina entre o Sistema Clientee os SGBDs deve ser instalado o aplicativo GABRIEL junto com o arquivo“Midas.dll”. Nesta máquina também devem ser instalados os drivers clientedo SGBDs, pois o aplicativo GABRIEL é visto pelos SGBDs com um sistema clientecomum.
Configuração do LUCAS-GABRIEL
O arquivo “LUCAS.dll” será configuradodentro do Sistema Cliente. Mas o aplicativo GABRIEL requer configuraçõesmanuais.
A figura 2 mostra a aparência do aplicativoGABRIEL quando ele for inicializado pela primeira.
Figura 2 – Aplicativo GABRIEL nãoconfigurado
Para configura o aplicativo GABRIEL siga ospassos abaixo:
1. Na“Caixa de edição Porta” informe a porta e na seqüência clique no “BotãoSetPorta”. Nossa configuração usou a porta “1010”, mas a sua porta pode serdiferente.
2. Na“Caixa de edição Servidor” informe o nome do servidor, na “Caixa deedição Endereço” informe o endereço do banco de dados, em seguida clique nobotão “Botão SetServidor”, repita este processo para todos osservidores. A nossa configuração usou 3 servidores de banco dedados e ficou como mostrado abaixo, ela serve de exemplo para a suaconfiguração:
a.
Endereço: 192.168.56.20:\Documents andSettings\Windows-VB\Desktop\BancoDados_1.FDB
b.
Endereço: 192.168.56.30:\Documents andSettings\Windows-VB\Desktop\BancoDados_2.FDB
c.
Endereço: 192.168.56.40:\Documents andSettings\Windows-VB\Desktop\BancoDados_3.FDB
3. Na“Caixa de edição Tabela” informe o nome da tabela que será distribuídaentre os bancos de dados, na “Caixa de edição Chave” informe o nome doatributo da tabela que deve ser tratada como chave primária, e na “Caixa deedição Gerador” informe o número que será usado como valor de inicializaçãoda chave primária. Na seqüência clique no “Botão SetTabela”, repitaeste processo para todas as tabelas. Para seguir os exemplos desteartigo, a sua configuração deve ficar igual a nossa:
a.
b.
c.
d.
Figura 3 – Aplicativo GABRIEL configurado
Criando o Formulário Principal do Projeto SistemaCliente
Crie um novo projeto, modifique apropriedade Name do formulário para FPrincipal. Insirana seção interface da Unit o código que estádestacado com a cor vermelha.
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs;
type
TFPrincipal = class(TForm)
private
{ Private declarations }
public
{ Public declarations }
ArqResposta: string;
end;
procedure LUCAS_SetAtivarSoquete(Ativar: Boolean);
stdcall; external 'LUCAS.dll';
procedure LUCAS_SetPorta(Porta: Integer);
stdcall; external 'LUCAS.dll';
procedure LUCAS_SetMediador(Endereco: string);
stdcall; external 'LUCAS.dll';
procedure LUCAS_SetResposta(Endereco: string);
stdcall; external 'LUCAS.dll';
procedure LUCAS_SetConfiguracao(Porta: Integer; EnderecoMediador,
EnderecoResposta: string);
stdcall; external 'LUCAS.dll';
procedure LUCAS_SetTabela(Tabela: string);
stdcall; external 'LUCAS.dll';
procedure LUCAS_SetChaveEstrangeira(Tabela, Campo: string);
stdcall; external 'LUCAS.dll';
procedure LUCAS_SetDados(Campo, Valor: string);
stdcall; external 'LUCAS.dll';
procedure LUCAS_Clear;
stdcall; external 'LUCAS.dll';
procedure LUCAS_ClearChaveEstrangeiraTudo;
stdcall; external 'LUCAS.dll';
procedure LUCAS_ClearChaveEstrangeira(Tabela, Campo: string);
stdcall; external 'LUCAS.dll';
procedure LUCAS_ClearDadosTudo;
stdcall; external 'LUCAS.dll';
procedure LUCAS_ClearDados(Campo: string);
stdcall; external 'LUCAS.dll';
function LUCAS_GetSocketAtivo: Boolean;
stdcall; external 'LUCAS.dll';
function LUCAS_GetSocketAtivar: Boolean;
stdcall; external 'LUCAS.dll';
function LUCAS_GetPorta: Integer;
stdcall; external 'LUCAS.dll';
function LUCAS_GetMediador: string;
stdcall; external 'LUCAS.dll';
function LUCAS_GetResposta: string;
stdcall; external 'LUCAS.dll';
function LUCAS_GetTabela: string;
stdcall; external 'LUCAS.dll';
function LUCAS_GetChaveEstrangeira: string;
stdcall; external 'LUCAS.dll';
function LUCAS_GetDados: string;
stdcall; external 'LUCAS.dll';
function LUCAS_GetPendente: Integer;
stdcall; external 'LUCAS.dll';
function LUCAS_GetStatusDescr(var Status: string): Integer;
stdcall; external 'LUCAS.dll';
function LUCAS_GetStatus: Integer;
stdcall; external 'LUCAS.dll';
procedure LUCAS_SetIncluir(Requisitar: Boolean);
stdcall; external 'LUCAS.dll';
procedure LUCAS_SetAlterar(Condicao: string; Requisitar: Boolean);
stdcall; external 'LUCAS.dll';
procedure LUCAS_SetExcluir(Condicao: string; Requisitar: Boolean);
stdcall; external 'LUCAS.dll';
procedure LUCAS_SetConsultar(Condicao: string);
stdcall; external 'LUCAS.dll';
procedure LUCAS_SetConsultarSQL(ComandoSelect: string);
stdcall; external 'LUCAS.dll';
procedure LUCAS_SetRequisitar;
stdcall; external 'LUCAS.dll';
var
FPrincipal: TFPrincipal;
No evento OnShow do FPrincipalentre com o código abaixo:
procedure TFPrincipal.FormShow(Sender: TObject);
begin
// Desativando conexão com o GABRIEL
LUCAS_SetAtivarSoquete(False);
// Configurando endereço do mediador
// Este endereço foi usado no nosso projeto, mas o seu pode ser diferente
LUCAS_SetMediador('192.168.56.10');
// Configurando porta de comunicação
// Esta porta foi a configura na “Caixa de edição Porta”
// do aplicativo GABRIEL, mas a sua pode ser diferente
LUCAS_SetPorta(1010);
// Ativando conexão com o GABRIEL
LUCAS_SetAtivarSoquete(True);
// Nome do arquivo de retorno
// Este nome é padrão e pode ficar numa constante do sistema
ArqResposta := 'Consulta.xml';
// Configurando endereço do arquivo com retorno da consulta
LUCAS_SetResposta( ExtractFilePath(Application.Exename) );
end;
Coloque no formulário um componente TMainMenue configure-o como mostrado na figura 4.
Figura 4 – Menu do Sistema Cliente
Observação:A construção do Sistema Clientefocará o uso do middleware LUCAS-GABRIEL, logo nem sempre serão usadas as boaspráticas da programação por questões didáticas.
Salve o formulário com o nome de UPrincipale o projeto com o nome de SistemaCliente.
Criando o Formulário FClienteInc
Crie um formulário e configure e coloque oscomponentes solicitados:
| |||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||
|
| ||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||
|
| ||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||
|
|
Figura 5 – Formulário FClienteInc
Faça referência a UPrincipalna uses da seção interface. No evento OnClickdo BtnAbandonar insira o comando Close, e no OnClickdo BtnGravar entre com o código abaixo:
procedure TFClienteInc.BtnGravarClick(Sender: TObject);
begin
// Limpa todas as solicitações pendentes
LUCAS_Clear;
// Informa que a tabela Cliente receberá um novo registro
LUCAS_SetTabela('Cliente');
// Atribui o valor do EdNome.Text ao campo Nome
LUCAS_SetDados('Nome', EdNome.Text);
// Atribui o valor do EdLimite_Credito.Text ao campo Limite_Credito
LUCAS_SetDados('Limite_Credito', EdLimite_Credito.Text);
// Envia a solicitação de inclusão
LUCAS_SetIncluir(True);
ShowMessage('Rotina concluída');
end;
Salve o arquivo com o nome UClienteInc.Pronto, este código é suficiente para incluir dados na tabela Cliente.Para testá-lo insira uma chamada ao formulário FClienteInc nomenu do FPrincipal, compile o projeto e efetue algumas inclusões.Depois verifique os bancos de dados para constatar que os registros inseridosforam distribuídos entres eles de maneira uniforme, ou seja, se você tem 3banco de dados configurado no GABRIEL e inseriu 12 registros, cada banco dedados terá 4 registros.
Criando a Unit UThreadConsulta
Antes de efetuar uma atualização ouexclusão é necessário resgatar as informações do banco de dados (consultar ocadastro) verificar os dados existentes, e só então efetuar a rotina desejada (atualizaçãoou exclusão).
Quando o LUCAS faz uma consulta ao GABRIEL,ele recebe um arquivo XML com o nome de Consulta.XML, dentrodeste arquivo vêm os dados de retorno da consulta.
A classe TThreadConsulta seráresponsável por receber o arquivo Consulta.xml e inserir os dadosnuma matriz do tipo String de duas dimensões. Posteriormente ficará maisclaro o uso desta classe.
Para implementar esta classe crie uma Unitsem formulário, entre com o código mostrado abaixo e depois a salve com o nomede UThreadConsulta.
unit UThreadConsulta;
interface
uses
Classes, SysUtils, Dialogs, Controls, XMLDoc, XMLIntf, UPrincipal;
type
TMatriz = array of array of string;
TMethodParameter = procedure(Matriz: TMatriz) of Object;
TThreadConsulta = class(TThread)
private
MethodParameter: TMethodParameter;
procedure CarregarConsulta;
public
constructor Create(CreateSuspended: Boolean);
procedure Execute; override;
procedure SetMethodParameter(const MethodParameter: TMethodParameter);
end;
implementation
{ TThreadConsulta }
procedure TThreadConsulta.CarregarConsulta;
var
Values: TMatriz;
XMLDocument: TXMLDocument;
NodeRegistro: IXMLNode;
Registro, Coluna, I: Integer;
begin
try
XMLDocument := TXMLDocument.Create(FPrincipal);
XMLDocument.FileName := FPrincipal.ArqResposta;
XMLDocument.Active := True;
try
NodeRegistro :=
XMLDocument.DocumentElement.ChildNodes.FindNode('registro');
Values := nil;
if ( Assigned(NodeRegistro) ) then
begin
Registro := 0;
Coluna := NodeRegistro.ChildNodes.Count;
while ( Assigned(NodeRegistro) ) do
begin
Inc(Registro);
SetLength(Values, Registro, Coluna);
for I := 0 to Pred(Coluna) do
Values[(Registro-1), I] :=
NodeRegistro.ChildNodes.Get(I).Text;
NodeRegistro := NodeRegistro.NextSibling;
end;
end;
MethodParameter(Values);
finally
XMLDocument.Active := False;
end;
finally
DeleteFile(FPrincipal.ArqResposta);
end;
end;
constructor TThreadConsulta.Create(CreateSuspended: Boolean);
begin
inherited;
FreeOnTerminate := True;
end;
procedure TThreadConsulta.Execute;
var
Espera: Integer;
begin
inherited;
Espera := 0;
while not( FileExists(FPrincipal.ArqResposta) ) do
begin
Inc(Espera);
Sleep(1000);
if (Espera > 10) then
begin
if ( MessageDlg('Arquivo ainda não recebido.' +#13#13+
'Deseja continuar aguardando?',
mtConfirmation, [mbYes, mbNo], 0) <> mrYes ) then Exit;
Espera := 0;
end;
end;
Synchronize(CarregarConsulta);
end;
procedure TThreadConsulta.SetMethodParameter(
const MethodParameter: TMethodParameter);
begin
Self.MethodParameter := MethodParameter;
end;
end.
Criando o Formulário FClienteAlt
O layout do formulário para alterar osdados é semelhante ao do FClienteInc, nele será colocado mais um TLabel(Name=LblID) e TEdit (Name=EdID) comomostrado na figura 6.
Figura 6 – Formulário FClienteAlt
Faça referência a UPrincipale a UThreadConsulta na uses da seção interface.No evento OnClick do BtnAbandonar insira o comando Close,e no OnClick do BtnGravar entre com o códigoabaixo:
procedure TFClienteAlt.BtnGravarClick(Sender: TObject);
begin
// Limpa todas as solicitações pendentes
LUCAS_Clear;
// Informa que a tabela Cliente receberá uma atualização
LUCAS_SetTabela('Cliente');
// Atribui o valor do EdNome.Text ao campo Nome
LUCAS_SetDados('Nome', EdNome.Text);
// Atribui o valor do EdLimite_Credito.Text ao campo Limite_Credito
LUCAS_SetDados('Limite_Credito', EdLimite_Credito.Text);
// Envia a solicitação de alteração
// para o registro com ID igual ao valor EdID.Text
LUCAS_SetAlterar('ID = ' + EdID.Text, True);
ShowMessage('Rotina concluída');
end;
No evento OnExit do EdIDinsira o código abaixo:
procedure TFClienteAlt.EdIDExit(Sender: TObject);
var
Consulta: TThreadConsulta;
begin
if ( FileExists(FPrincipal.ArqResposta) )
then DeleteFile(FPrincipal.ArqResposta);
// Limpa todas as solicitações pendentes
LUCAS_Clear;
// Informa que a tabela Cliente receberá uma consulta
LUCAS_SetTabela('Cliente');
// No retorno da consulta deve constar o campo ID
LUCAS_SetDados('ID', '');
// No retorno da consulta deve constar o campo Nome
LUCAS_SetDados('Nome', '');
// No retorno da consulta deve constar o campo Limite_Credito
LUCAS_SetDados('Limite_Credito', '');
// Envia a solicitação de consulta
// para o registro com ID igual ao valor EdID.Text
LUCAS_SetConsultar('ID = ' + EdID.Text);
// Processa o arquivo "Consulta.xml" como uma Thread
Consulta := TThreadConsulta.Create(True);
Consulta.SetMethodParameter(CarregarConsulta);
Consulta.Resume;
end;
Declare o método “procedureCarregarConsulta(Matriz: TMatriz);” como privado da classe TFClienteAlte entre com a implementação abaixo:
// Este método é chamado pela classe TThreadConsulta após
// a mesma receber o arquivo "Consulta.xml", efetuar o
// devido tratamento dos dados e inserir os valores de
// retorno dentro do parâmetro Matriz
procedure TFClienteAlt.CarregarConsulta(Matriz: TMatriz);
begin
if not( Assigned(Matriz) ) then
begin
MessageDlg('Código(ID) não localizado!', mtWarning, [mbOk], 0);
EdID.SetFocus;
Exit;
end;
EdNome.Text := Matriz[0, 1];
EdLimite_Credito.Text := Matriz[0, 2];
end;
Salve o arquivo com o nome UClienteAlt,insira uma chamada ao formulário FClienteAlt no menu do FPrincipal,compile o projeto e efetue algumas alterações para testar.
Criando o Formulário FClienteExc
Crie um formulário e configure e coloque oscomponentes solicitados:
| |||||||||||||||||||||||||
| |||||||||||||||||||||||||
|
| ||||||||||||||||||||||||
| |||||||||||||||||||||||||
|
| ||||||||||||||||||||||||
|
| ||||||||||||||||||||||||
|
Figura 7 – Formulário FClienteExc
Clique com o botão inverso do mouse sobrecomponente CDSCliente, no menu rápido escolha a opção “FieldsEditor...”. Clique botão inverso do mouse sobre a janela do FieldEditor e escolha a opção “New field...” e preencha comomostrado na figura 8 e clique no botão OK.
Figura 8 – Criando o campo ID do CDSCliente
Repita o processo mais duas vezes, mas preenchendode acordo com a figura 9.
Figura 9 – Criando os campos Nome eLimite_Credito do CDSCliente
Clique novamente no componente CDSClientecom o botão inverso do mouse e escolha a opção “Create DataSet” eem seguida atribua True para a propriedade Active.
Faça referência a UPrincipale a UThreadConsulta na uses da seção interface.No evento OnShow do FClienteExc entre com o códigoabaixo:
procedure TFClienteExc.FormShow(Sender: TObject);
var
Consulta: TThreadConsulta;
begin
CDSCliente.CancelUpdates;
if ( FileExists(FPrincipal.ArqResposta) )
then DeleteFile(FPrincipal.ArqResposta);
// Limpa todas as solicitações pendentes
LUCAS_Clear;
// Informa que a tabela Cliente receberá uma consulta
LUCAS_SetTabela('Cliente');
// No retorno da consulta deve constar o campo ID
LUCAS_SetDados('ID', '');
// No retorno da consulta deve constar o campo Nome
LUCAS_SetDados('Nome', '');
// No retorno da consulta deve constar o campo Limite_Credito
LUCAS_SetDados('Limite_Credito', '');
// Envia a solicitação de consulta
// para os registros com ID superior a 0
LUCAS_SetConsultar('ID > 0');
// Processa o arquivo "Consulta.xml" como uma Thread
Consulta := TThreadConsulta.Create(True);
Consulta.SetMethodParameter(CarregarConsulta);
Consulta.Resume;
end;
No evento OnClick do BtnAbandonarinsira o comando Close, e no OnClick do BtnExcluirentre com o código abaixo:
procedure TFClienteExc.BtnExcluirClick(Sender: TObject);
begin
// Limpa todas as solicitações pendentes
LUCAS_Clear;
// Informa que a tabela Cliente receberá uma exclusão
LUCAS_SetTabela('Cliente');
// Envia a solicitação de exclusão
// para o registro com ID igual ao ID do registro atual do CDSCliente
LUCAS_SetExcluir('ID = ' + CDSCliente.FieldByName('ID').AsString, True);
FormShow(nil);
end;
Declare o método “procedureCarregarConsulta(Matriz: TMatriz);” como privado da classe TFClienteExce entre com a implementação abaixo:
// Este método é chamado pela classe TThreadConsulta após
// a mesma receber o arquivo "Consulta.xml", efetuar o
// devido tratamento dos dados e inserir os valores de
// retorno dentro do parâmetro Matriz
procedure TFClienteExc.CarregarConsulta;
var
Registro: Integer;
begin
CDSCliente.CancelUpdates;
if not( Assigned(Matriz) ) then
begin
MessageDlg('Tabela está fazia!', mtWarning, [mbOk], 0);
Exit;
end;
for Registro := 0 to Pred( Length(Matriz) ) do
begin
CDSCliente.Append;
CDSCliente.FieldByName('ID').AsString :=
Matriz[Registro, 0];
CDSCliente.FieldByName('Nome').AsString :=
Matriz[Registro, 1];
CDSCliente.FieldByName('Limite_Credito').AsString :=
Matriz[Registro, 2];
CDSCliente.Post;
end;
end;
Salve o arquivo com o nome UClienteExc,insira uma chamada ao formulário FClienteExc no menu do FPrincipal,compile o projeto e efetue algumas exclusões para testar.
Artigocriado por http://www.cassic.com.br/
Qualquerdúvida nos envie um e-mail para suporte@cassic.com.br