Demora em processo muito grande

20/06/2017

0

Bom dia,

Tenho um aplicativo para consultar as vendas de uma banco postgre, jogo os registros na query e faço um loop pegando os produtos e pagamentos de cada venda e montando um JSON, após montado o JSON realizo um post em um webservice de outro software de terceiro(erp principal), tudo isso coloquei dentro do processo de uma thread, consultando pelo log a cada 4 há 6 segundos monta o json e faz o post no webservice, a situção é que em 1 mes de venda de apenas um loja tenho em media 25.000 vendas isso está dando mais de 24 horas executando.
Com isso comecei a caçado sobre pool de thread, parallel e tasks, na verdade não sei o que usar e qual maneira corretamente, já vi vários videos, li artigos, mas não consegui materializar tudo em linha de codigo com meu problema, na verdade estava pretendendo enviar 100 posts no webservice, pois vários ao mesmo tempo a api consegue processar rapidamente, então o que preciso é de uma ajuda se realizo com pool de tread, parallel ou task...

Para terem uma noção melhor montei um exemplo que chega bem proximo do modelo funcional do processo, como existe varias consultas e validação, creio que não compensa postar o codigo....
unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;

type
   MinhaThread = class(TThread)
   procedure Execute; override;
   procedure Verifica;
   procedure Fechar;
   Private
   constructor Create();
end;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
    thread: MinhaThread;
  public
    { Public declarations }
    procedure consultaProcesso(Sender: TObject);
    procedure postJSON(JSON:String);
  end;

var
  Form1: TForm1;
  I : Integer;
  JSON:String;

implementation

{$R *.dfm}

{ MinhaThread }

constructor MinhaThread.Create;
begin
inherited
  Create(True);
  FreeOnTerminate := True;
  Priority := tpLower;
  Resume;
end;

procedure MinhaThread.Execute;
Var Sender : TObject;
begin
   Synchronize(Verifica);
   Form1.consultaProcesso(Sender); // Executar Rotina ( Procedures )

   while not Terminated do
    begin
        Sleep (10);
        Terminate; // Finaliza a Thread
        Synchronize(Fechar);
    end;

end;

procedure MinhaThread.Fechar;
begin
  //application.terminate;
end;

procedure MinhaThread.Verifica;
begin
  Form1.Caption := 'EXECUTANDO...'+IntToStr(I);
end;

{ TForm1 }

procedure TForm1.Button1Click(Sender: TObject);
begin
  thread := MinhaThread.Create();
end;

procedure TForm1.consultaProcesso(Sender: TObject);
begin
  //exemplo com o for
  // porem aqui eu percorro a query, passo 
  // para a variavel JSON o json que está na query
  // e chamo o metodo post
  for I := 0 to 100 do
    begin
      postJSON(JSON);
    end;
end;

procedure TForm1.postJSON(JSON: String);
begin
  //faz um post pelo idHTTP;
  Memo1.Lines.Add(DateTimeToStr(now)+ ' - Executando JSON '+IntToStr(I));
  //para simular um tempo de espera do retorno
  Sleep(1000);
  //retorno...
  Memo1.Lines.Add(DateTimeToStr(now)+ ' - Retorno JSON '+IntToStr(I));
end;

end.
Robson Morais

Robson Morais

Responder

Posts

20/06/2017

Fernando Oliveira

Boa tarde, sou novo por aqui e não tenho tanta experiência quanto outros por aqui, mas já desenvolvi vários softwares sozinho e passei por um problema parecido.
Portanto espero que minha dica ajude...

Veja bem, eu evito ao máximo usar sleep, pois como sabe ele paralisa o programa, só uso ele em situações específicas, prefiro usar um timer de acordo o que for necessário.
Bom a minha aplicação assim como a sua, precisa atualizar várias informações de tempos em tempos entre dois bancos de dados diferentes, então para não ter que fazer isso a cada segundo (o que toma processamento e as vezes vai juntando "sujeira" na memória de pendendo do processo), eu adicionei um campo em uma das tabelas onde o sistema verifica se houve sincronização entre os bancos.

Então a cada x segundos o sistema verifica apenas essa tabela (tabela sync booleana), caso ainda não tenham sido sincronizados os dados ai sim ele faz as transferências, caso contrário ele não faz nada.

Dessa forma consegui reduzir o consumo de processamento.
Espero que ajude.
Abrçs.
Responder

20/06/2017

Fernando Oliveira

Apenas corrigindo um pequeno erro... onde disse tabela, eu quis diz um campo (Campo Sync Booleano) em uma tabela.

Boa tarde, sou novo por aqui e não tenho tanta experiência quanto outros por aqui, mas já desenvolvi vários softwares sozinho e passei por um problema parecido.
Portanto espero que minha dica ajude...

Veja bem, eu evito ao máximo usar sleep, pois como sabe ele paralisa o programa, só uso ele em situações específicas, prefiro usar um timer de acordo o que for necessário.
Bom a minha aplicação assim como a sua, precisa atualizar várias informações de tempos em tempos entre dois bancos de dados diferentes, então para não ter que fazer isso a cada segundo (o que toma processamento e as vezes vai juntando "sujeira" na memória de pendendo do processo), eu adicionei um campo em uma das tabelas onde o sistema verifica se houve sincronização entre os bancos.

Então a cada x segundos o sistema verifica apenas esse campo (campo sync booleano) em uma tabela do banco onde conteem as informações a serem extraídas , caso ainda não tenham sido sincronizados os dados ai sim ele faz as transferências e insere nessa tabela(sync) um true dizendo que aquela linha já foi exportada, caso contrário ele não faz nada.

Dessa forma consegui reduzir o consumo de processamento.
Espero que ajude.
Abrçs.
Responder

20/06/2017

Robson Morais

Obrigado pela resposta, mas não seria esse caso mesmo, a carga que estou realizando é historico, então tenho que mandar tudo, depois o processo ira rodar 1 vez por dia por causa de fechamentos financeiros e fiscais, o que preciso aumentar esse processamento....
Responder

20/06/2017

Emerson Nascimento

acredito que haja um problema de conceito: se você trabalha com threads, você precisa executar múltiplas vezes a thread, senão não faz sentido.

não sei se entendi o seu processo, mas deveria ser algo assim:

procedure TForm1.Button1Click(Sender: TObject);
begin
	query.open;
	while not query.eof
	begin
		MinhaThread.Create( query.registro )
		query.next;
	end

end

da forma apresentada, será criada uma thread para cada registro que será processado. e serão executadas simultaneamente.
você precisara modificar a sua thread para receber o parâmetro e efetuar todo o tratamento de cada registro dentro da thread, juntamente com o idHTTP post e a sincronização das respostas na tela.

entendeu?
Responder

20/06/2017

Robson Morais

entendi, porque dessa forma a parte mais pesada que é o post e o retorno fica por conta da thread....
porém dentro da thread eu consigo tratar um log (txt) para todas as threads ? ou seria melhor mesmo por banco ?
Responder

21/06/2017

Emerson Nascimento

entendi, porque dessa forma a parte mais pesada que é o post e o retorno fica por conta da thread....
porém dentro da thread eu consigo tratar um log (txt) para todas as threads ? ou seria melhor mesmo por banco ?


você consegue, sim, tratar o log num arquivo txt. o nome do arquivo pode ser um dos parâmetros da thread, por exemplo. mas será necessário proteger o código para não acontecer falha na abertura simultânea do arquivo.
se puder gravar o log no banco fica melhor, porque cada iteração será um novo registro, não havendo problema de bloqueio.
Responder

21/06/2017

Robson Morais

estou tentando fazer dessa forma;
procedure TfrmPSyncBack.Button2Click(Sender: TObject);
var linha: TStringList;
    a, nrAcu, total, ts :integer;
    aux : Currency;
    lista : TStringList;
    NomeDoLog, texto: string;
    Arquivo: TextFile;
    HTTP: TIdHTTP;
    RequestBody: TStream;
    ResponseBody, retorno, nrCoo, idCaixa, cdEmp, JSON: string;
    qryTeste: TFDQuery;
begin

      linha := TStringList.Create;
      lista := TStringList.Create;

 //gerei um arquivo(txt) com a extensão json para carregar e executar no loop;
 //o arquivo contem o numero do cupom, id caixa, empresa e o json separados por |
 //esses parametros uso para gravar junto com o retorno.

 NomeDoLog := ExtractFilePath(Application.ExeName) + 'log\\jsonPSyncBack_20062017_1933.json';
 AssignFile(Arquivo, NomeDoLog);
 {$I-}
 Reset(Arquivo);
 {$I+}

 lista.LoadFromFile(ExtractFilePath(Application.ExeName) + 'log\\jsonPSyncBack_20062017_1933.json');

 while (not eof(Arquivo)) do
    begin
                    readln(Arquivo, texto);
                     linha.Delimiter := '|';
                     linha.StrictDelimiter := true;
                     linha.DelimitedText := texto;
                     nrCoo := linha[0];
                     idCaixa := linha[1];
                     cdEmp := linha[2];
                     JSON := linha[3];

                     MyThread := TThread.CreateAnonymousThread(procedure begin

                          qryTeste := nil;
                          qryTeste := TFDQuery.Create(qryTeste);
                          qryTeste.Connection := DtmPSyncBack.conPlatinum;

                                  HTTP := TIdHTTP.Create;
                                  try
                                    try

                                     RequestBody := TStringStream.Create(JSON,TEncoding.UTF8);
                                      try
                                        HTTP.Request.Accept := 'application/json';
                                        HTTP.Request.ContentType := 'application/json;charset=utf-8;';
                                        HTTP.Request.BasicAuthentication := true;
                                        HTTP.Request.Connection  := 'keep-alive';

                                        ResponseBody := UTF8ToString(HTTP.Post('http://user:password@host:port/api/teste/pedido/inclui',RequestBody));

                                        qryTeste.Close;
                                        qryTeste.SQL.Text := 'insert into tbl_adm_logintegracao (cd_empresa,nr_coo,nr_idcaixa,tp_log,ds_log,ds_json,ds_pedidov,dt_cadastro)'+
                                                            ' values ('+#39 +cdEmp+#39+','+#39 +nrCoo+#39+','+#39 +idCaixa+#39+','+#39 +'OK'+#39+','+#39 +'OK'+#39+','+
                                                            #39 +JSON+#39+','+#39 +ResponseBody+#39+',CURRENT_TIMESTAMP)';
                                        qryTeste.ExecSQL;

                                      finally
                                        RequestBody.Free;
                                      end;
                                    except
                                      on E: EIdHTTPProtocolException do
                                      begin

                                        qryTeste.Close;

                                        qryTeste.SQL.Text := 'insert into tbl_adm_logintegracao (cd_empresa,nr_coo,nr_idcaixa,tp_log,ds_log,ds_json,dt_cadastro)'+
                                                            ' values ('+#39 +cdEmp+#39+','+#39 +nrCoo+#39+','+#39 +idCaixa+#39+','+#39 +'ERRO'+#39+','+#39+E.ErrorMessage+#39+','+
                                                            #39 +JSON+#39+',CURRENT_TIMESTAMP)';
                                        qryTeste.ExecSQL;

                                      end;
                                      on E: Exception do
                                      begin
                                      end;
                                    end;
                                  finally
                                    HTTP.Free;
                                  end;


                     end);
          end;

         CloseFile(Arquivo);
         linha := nil;

end;


dados do json
4|83653|22|[{"cod_pedidov":"4.83653.22","vitrine":4,"data_emissao":"2017-04-06","data_entrega":"2017-04-06","total":46.97,"quantidade":3,"n_serie":"BE091110100011237675","modelo":"ECF","numecf":22,"nota":83653,"serie":"1","faturar":"I","produtos":[{"item":1,"quantidade":1,"preço":19.99},{"item":2,"quantidade":1,"preço":13.99},{"barra":"2251202415835","item":3,"quantidade":1,"preço":12.99}],"lancamentos":[{"data_vencimento":"2017-04-06","valor_inicial":46.97,"numparc":11,"desc_tipo":"NALIN CREDITO","desc_bandeira":"NALIN","autorizacao":"400786","nsu":"000000444918"}],"dados_cliente":[{"nome":"CONSUMIDOR FINAL","vitrine":4,"endereco":[{"cep":"25225660","numero":""}]}]}]
4|180549|20|[{"cod_pedidov":"4.180549.20","vitrine":4,"data_emissao":"2017-04-10","data_entrega":"2017-04-10","total":68.93,"quantidade":7,"n_serie":"BE091110100011237014","modelo":"ECF","numecf":20,"nota":180549,"serie":"1","faturar":"I","produtos":[{"item":1,"quantidade":1,"preço":29.99},{"barra":"2251204175348","item":2,"quantidade":1,"preço":2.99},{"barra":"2251204175348","item":3,"quantidade":1,"preço":2.99},{"barra":"2251204175348","item":4,"quantidade":1,"preço":2.99},{"barra":"2251203876925","item":5,"quantidade":1,"preço":9.99},{"barra":"2251203876925","item":6,"quantidade":1,"preço":9.99},{"barra":"2251203876925","item":7,"quantidade":1,"preço":9.99}],"lancamentos":[{"data_vencimento":"2017-04-10","valor_inicial":68.93,"numparc":0,"desc_tipo":"DINHEIRO"}],"dados_cliente":[{"nome":"CONSUMIDOR FINAL","vitrine":4,"endereco":[{"cep":"25225660","numero":""}]}]}]
4|179469|20|[{"cod_pedidov":"4.179469.20","vitrine":4,"data_emissao":"2017-04-01","data_entrega":"2017-04-01","total":19.99,"quantidade":1,"n_serie":"BE091110100011237014","modelo":"ECF","numecf":20,"nota":179469,"serie":"1","faturar":"I","produtos":[{"barra":"2251203757378","item":1,"quantidade":1,"preço":19.99}],"lancamentos":[{"data_vencimento":"2017-04-01","valor_inicial":19.99,"numparc":0,"desc_tipo":"DINHEIRO"}],"dados_cliente":[{"nome":"CONSUMIDOR FINAL","vitrine":4,"endereco":[{"cep":"25225660","numero":""}]}]}]
4|84197|22|[{"cod_pedidov":"4.84197.22","vitrine":4,"data_emissao":"2017-04-10","data_entrega":"2017-04-10","total":201.51,"quantidade":9,"n_serie":"BE091110100011237675","modelo":"ECF","numecf":22,"nota":84197,"serie":"1","faturar":"I","produtos":[{"item":1,"quantidade":1,"preço":9.83444},{"item":2,"quantidade":1,"preço":9.83444},{"barra":"2251204456515","item":3,"quantidade":1,"preço":13.83444},{"item":4,"quantidade":1,"preço":38.83444},{"item":5,"quantidade":1,"preço":13.83444},{"barra":"2251204708102","item":6,"quantidade":1,"preço":28.83444},{"item":7,"quantidade":1,"preço":18.83444},{"item":8,"quantidade":1,"preço":38.83444},{"item":9,"quantidade":1,"preço":28.83444}],"lancamentos":[{"data_vencimento":"2017-04-10","valor_inicial":201.51,"numparc":1,"desc_tipo":"NALIN CREDITO","desc_bandeira":"NALIN","autorizacao":"400030","nsu":"000000449536"}],"dados_cliente":[{"nome":"CONSUMIDOR FINAL","vitrine":4,"endereco":[{"cep":"25225660","numero":""}]}]}]


fazendo dessa forma acima....o processado fica 100% e aparentemente não está executando, porque não tenho retorno dos inserts....dos retornos do post, depois de uns 10 minutos eu tenho erro de Runtime error 214 at 01B001E.

Creio que teria que fazer assim..... gerar 10 Threads com o post dentro....aguardar o retorno das 10, pois teoricamente as 10 está rodando ao mesmo tempo, então seria mais rapido que rodando 1 por vez e aguardando o retorno.... depois assim que retornar 1 já entra na fila outra....
Responder

21/06/2017

Emerson Nascimento

deveria ser algo assim:

thread:
type
	MinhaThread = class(TThread)
	private
		conexao: TSQLConnetion;
		qryTeste: TFDQuery;
		HTTP: TIdHTTP;
		RequestBody: TStream;
		nrCoo, idCaixa, cdEmp, JSON, ResponseBody: string;
	protected
		procedure Execute; override;
	public
		constructor Create(const sqlConexao: TSQLConnection; strnrCoo, stridCaixa, strcdEmp, strJSON: string);
		destructor Destroy; override;
	end;


{ MinhaThread }

constructor MinhaThread.Create(const sqlConexao: TSQLConnection; strnrCoo, stridCaixa, strcdEmp, strJSON: string);
begin
	inherited Create( True ); // cria suspensa

	// atribui o conteúdo dos parâmetros às 'propriedades' da thread
	Self.conexao := sqlConexao;
	Self.nrCoo := strnrCoo;
	Self.idCaixa := stridCaixa
	Self.cdEmp := strcdEmp
	Self.JSON := strJSON

	// cria os objetos necessários
	Self.qryTeste := TFDQuery.Create(nil);
	Self.qryTeste.Connection := Self.conexao
	Self.HTTP := TIdHTTP.Create;

	// configura as propriedades da thread
	FreeOnTerminate := True;
	Priority := tpLower;

	// executa a thread
	Resume;
end;

procedure MinhaThread.Execute;
begin
	try
		Self.RequestBody := TStringStream.Create(Self.JSON,TEncoding.UTF8);

		Self.HTTP.Request.Accept := 'application/json';
		Self.HTTP.Request.ContentType := 'application/json;charset=utf-8;';
		Self.HTTP.Request.BasicAuthentication := true;
		Self.HTTP.Request.Connection := 'keep-alive';

		Self.ResponseBody := UTF8ToString(HTTP.Post('http://user:password@host:port/api/teste/pedido/inclui',Self.RequestBody));

		Self.qryTeste.SQL.Text := 'insert into tbl_adm_logintegracao (cd_empresa,nr_coo,nr_idcaixa,tp_log,ds_log,ds_json,ds_pedidov,dt_cadastro)'+
					' values ('+#39 +Self.cdEmp+#39+','+#39 +Self.nrCoo+#39+','+#39 +Self.idCaixa+#39+','+#39 +'OK'+#39+','+#39 +'OK'+#39+','+
					#39 +Self.JSON+#39+','+#39+Self.ResponseBody+#39+',CURRENT_TIMESTAMP)';
		Self.qryTeste.ExecSQL;
	except
		on E:Exception do
		begin
			Self.qryTeste.SQL.Text := 'insert into tbl_adm_logintegracao (cd_empresa,nr_coo,nr_idcaixa,tp_log,ds_log,ds_json,dt_cadastro)'+
						' values ('+#39 +Self.cdEmp+#39+','+#39 +Self.nrCoo+#39+','+#39 +Self.idCaixa+#39+','+#39 +'ERRO'+#39+','+#39+E.ErrorMessage+#39+','+
						#39 +Self.JSON+#39+',CURRENT_TIMESTAMP)';
			Self.qryTeste.ExecSQL;
		end;
	end;
end;


destructor MinhaThread.Destroy;
begin
	FreeAndNil(Self.qryTeste);
	FreeAndNil(Self.HTTP);
	FreeAndNil(Self.RequestBody);
	FreeAndNil(Self.ResponseBody);

	inherited Destroy;
end;



exemplo de uso da thread:
procedure TfrmPSyncBack.Button2Click(Sender: TObject);
var
	linha, lista: TStringList;
	NomeDoLog, texto: string;
	Arquivo: TextFile;
	nrCoo, idCaixa, cdEmp, JSON: string;
begin
	linha := TStringList.Create;
	lista := TStringList.Create;

	//gerei um arquivo(txt) com a extensão json para carregar e executar no loop;
	//o arquivo contem o numero do cupom, id caixa, empresa e o json separados por |
	//esses parametros uso para gravar junto com o retorno.

	NomeDoLog := ExtractFilePath(Application.ExeName) + 'log\\\\jsonPSyncBack_20062017_1933.json';
	AssignFile(Arquivo, NomeDoLog);

	{$I-}
	Reset(Arquivo);
	{$I+}

	lista.LoadFromFile(ExtractFilePath(Application.ExeName) + 'log\\\\jsonPSyncBack_20062017_1933.json');

	while (not eof(Arquivo)) do
	begin
		readln(Arquivo, texto);
		linha.Delimiter := '|';
		linha.StrictDelimiter := true;
		linha.DelimitedText := texto;

		nrCoo := linha[0];
		idCaixa := linha[1];
		cdEmp := linha[2];
		JSON := linha[3];

		MinhaThread.Create( DtmPSyncBack.conPlatinum, nrCoo, idCaixa, cdEmp, JSON );
	end;

	CloseFile(Arquivo);
	linha := nil;
end;


eu fiz tudo pelo bloco de notas (estou sem o Delphi na máquina) então pode ser que necessite corrigir erros no código.
Responder

21/06/2017

Robson Morais

boa tarde, unico ajustes que realizei foi no create, utilizei de um datamodule que ja tenho toda a conexão com o firebird, porque não consegui criar em tempo de execução...
constructor MinhaThread.Create(strnrCoo,
  stridCaixa, strcdEmp, strJSON: string);
begin
  inherited Create(True); // cria suspensa

  Self.nrCoo := strnrCoo;
  Self.idCaixa := stridCaixa;
  Self.cdEmp := strcdEmp;
  Self.JSON := strJSON;

  // cria os objetos necessários
    Self.qryTeste := TFDQuery.Create(nil);
    Self.qryTeste.Connection := DtmPSyncBack.conPlatinum;
    Self.HTTP := TIdHTTP.Create;

    // configura as propriedades da thread
    FreeOnTerminate := True;
    Priority := tpLower;

   // executa a thread
  Resume;
end;


mas acredito que o erro que está dando não seja por isso....

Thread creation erro: Espaço insuficiente de armazenamento para processar este comando.

o Arquivo tem mais de 5.000 linhas tenho casos de arquivo com mais de 50.000 linhas, então acho que deve estar dando estouro por isso...
Responder

Assista grátis a nossa aula inaugural

Assitir aula

Saiba por que programar é uma questão de
sobrevivência e como aprender sem riscos

Assistir agora

Utilizamos cookies para fornecer uma melhor experiência para nossos usuários, consulte nossa política de privacidade.

Aceitar