Demora em processo muito grande
20/06/2017
0
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
Posts
20/06/2017
Fernando Oliveira
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.
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 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.
20/06/2017
Robson Morais
20/06/2017
Emerson Nascimento
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?
20/06/2017
Robson Morais
porém dentro da thread eu consigo tratar um log (txt) para todas as threads ? ou seria melhor mesmo por banco ?
21/06/2017
Emerson Nascimento
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.
21/06/2017
Robson Morais
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....
21/06/2017
Emerson Nascimento
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.
21/06/2017
Robson Morais
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...
Clique aqui para fazer login e interagir na Comunidade :)