AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein Netzwerke WebModule & ADOConnection in Service -> Speicherübrelauf
Thema durchsuchen
Ansicht
Themen-Optionen

WebModule & ADOConnection in Service -> Speicherübrelauf

Ein Thema von markusef · begonnen am 13. Jan 2017 · letzter Beitrag vom 19. Jan 2017
Antwort Antwort
Seite 1 von 3  1 23      
markusef

Registriert seit: 9. Mai 2016
15 Beiträge
 
Delphi 10 Seattle Professional
 
#1

WebModule & ADOConnection in Service -> Speicherübrelauf

  Alt 13. Jan 2017, 13:31
Hallo,

aktuell sitze ich an einem Client - Server Projekt.

Zum Server:
Als erstes setzte ich den Webservice auf (TWebmodule in einen Win-Service gebettet). Dieser beantwortet diverse Get Anfragen je nach Action (Parameter) und sucht die jeweiligen Daten aus einer Datenbank heraus (die sich ebenfalls auf dem Server befindet). Bei jeder Action wird eine TADOConnection created, eine TADOQuery created, belegt und genutzt. Anschließend geschlossen und mit einem FreeAndNil beseitigt.
Soweit funktioniert auch alles recht fix und wie gewollt.

Zum Client:
Firemonkey Crossplatform App, fragt Daten vom Webservice via Delphi RESTClient/RESTRequest/RESTResponse ab, macht seinen Job wie gewollt.

Zum Problem:
Der Client fragt in festen Zyklen Daten vom Webservice ab, nun fiel mir auf das die Arbeitsspeicherauslastung des Webservices (im Task Manager als .exe angezeigt) von Request zu Request steigt.
Allerdings wird in jeder Action des Webservices wie oben bereits beschrieben alles sachgemäßig (meiner Meinung nach) geschlossen und bereinigt.

*Nebenbemerkung:
Wenn ich die ADOConnection weglasse, kann ich tausende Anfragen in ein paar Sekunden generieren und bekomme auch eine Antwort ohne das die Arbeitsspeicherauslastung auch nur um 100kb nach oben steigt.

Ich habe bereits versucht mit FastMM der Sache auf den Grund zu gehen, allerdings bisher erfolglos.

Habt ihr zufällig Ideen woran das liegen könnte ?
Ist evtl. das gesamte Konstrukt eher ungünstig ? Bin ein wenig ratlos, programmiere das erste mal eine Client-Server Anwendung.
Bisher waren die serverseitigen Komponenten bereits immer vorhanden.
Markus
  Mit Zitat antworten Zitat
nahpets
(Gast)

n/a Beiträge
 
#2

AW: WebModule & ADOConnection in Service -> Speicherübrelauf

  Alt 13. Jan 2017, 13:41
Quelltext?

So ist es eher ein Raten mit Hilfe der

Wenn Du die ADOConnetion weglassen kannst und das Ganze auch ohne die funktioniert, dann lass sie doch weg, sie wird ja dann wohl nicht benötigt.

Bei den ADO-Komponenten kann man entweder eine ADOConnection zuweisen oder einen Connectionstring angeben.

Wenn der angegeben ist, kann die ADOConnection entfallen. Sollte in dem WebService nur eine ADO-Komponente sein, so kann sie die Verbindung problemlos selbst herstellen.

Eine ADOConnection ist eigentlich nur dann sinnvoll, wenn man mehrere ADO-Komponenten nutzt und sie alle über eine Verbindung laufen sollen.
  Mit Zitat antworten Zitat
markusef

Registriert seit: 9. Mai 2016
15 Beiträge
 
Delphi 10 Seattle Professional
 
#3

AW: WebModule & ADOConnection in Service -> Speicherübrelauf

  Alt 13. Jan 2017, 13:57
Okay, wieder was gelernt mit der ADOConnection.
Grundlegend und simpel gesagt will ich pro Action was aus der Datenbank holen, schick verpacken und als Response an den Client senden.
Also könnte ich demnach eine TADOQuery pro Action mit einem ConnectionString abfeuern ?

Desweiteren fiel mir auf das, sobald der REST Client eine Anfrage schickt, in der Klasse Webmodule in das WebModuleCreate gesprungen wird, allerdings nur bei der ersten Anfrage, bei allen weiteren nicht.
Sobald der REST Client fertig ist bleibt demnach (so vermute ich) die Instanz des Webmodules bestehen.
Destroy wird erst ausgelöst wenn ich den Service komplett schließe.

*nicht wundern, es ist erstmal nur ein Testaufbau, deshalb teilweise die sinnlosen Variablenbelegungen.

Code:

TWebModuleMain = class(TWebModule)
    //--------------------------------------------------------------------------
    //Webservice für HTTP Requests
    //--------------------------------------------------------------------------
    //Default Web Handler, liefert HTML Rumpf für "ungewöhnliche" Anfragen
    procedure WebModule3DefaultHandlerAction   ( Sender: TObject;
                                                  Request: TWebRequest;
                                                  Response: TWebResponse;
                                                  var Handled: Boolean);

    //Webhandler für Requests
    procedure WebModuleMaintcmAction           ( Sender: TObject;
                                                  Request: TWebRequest;
                                                  Response: TWebResponse;
                                                  var Handled: Boolean);

    procedure WebModuleCreate(Sender: TObject);
    procedure WebModuleDestroy(Sender: TObject);

  private
    { Private-Deklarationen }

    procedure getNothing       ( Request: TWebRequest; Response: TWebResponse); //test für memory leak
    procedure send501(text : String);

  public
    { Public-Deklarationen }
  end;

var
  WebModuleClass : TComponentClass = TWebModuleMain;


implementation

{%CLASSGROUP 'Vcl.Controls.TControl'}

{$R *.dfm}

//Default path (no path infos, only url + port)
procedure TWebModuleMain.WebModule3DefaultHandlerAction(Sender: TObject;
  Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
begin
  Response.Content :=
    '<html>' +
    '<head><title>Webserver-Anwendung</title></head>' +
    '<body>Test Webservice'+
    '</body>' +
    '</html>';
end;

procedure TWebModuleMain.WebModuleCreate(Sender: TObject);
var
  test : STring;
begin
  test := 'abcdef';
end;

procedure TWebModuleMain.WebModuleDestroy(Sender: TObject);
var
  Test : String;
begin
  Test := 'abcerf';
end;

procedure TWebModuleMain.WebModuleMaintcmAction(Sender: TObject;
  Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
begin
  Handled := true;
  case Request.MethodType of
    mtGet:     tcmGet( Request, Response );
    mtPut:     send501('this function was not implemented');
    mtPost:    send501('this function was not implemented');
    mtDelete:  send501('this function was not implemented');
    else begin
      send501('this function was not implemented');
    end;
  end;
end;

procedure TWebModuleMain.getNothing(Request: TWebRequest;
  Response: TWebResponse);
var
  tst : String;
  ADOConnection : TADOConnection;
  ADOQuery     : TADOQuery;
begin
  try
    CoInitialize(nil);
    ADOConnection := TADOConnection.Create(nil);
    ADOConnection.ConnectionString := 'Provider=MSDASQL.1;Password=CASS2HAR;Persist Security Info=True;User ID=ADMIN;Data Source=MaxDb_GOLD1';
    ADOQuery := TADOQuery.Create(nil);
    ADOQuery.Connection := ADOConnection;

    ADOConnection.Connected := true;

    ADOQuery.SQL.Text := 'SELECT Name, Age FROM customer WHERE id=1';

    ADOQuery.Open;
    ADOQuery.First;

    tst := tst + ADOQuery.FieldByName('name').AsString;
    Response.StatusCode := 200;
    Response.Content := tst;
  finally
    FreeAndNil(ADOQuery);
    ADOConnection.Connected := false;
    FreeAndNil(ADOConnection);
    CoUninitialize;
  end;
end;

//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Help Classes
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++


procedure TWebModuleMain.tcmGet(Request: TWebRequest;
  Response: TWebResponse);
begin
  if not Request.QueryFields.Values['action'].IsEmpty then
  begin
    if Request.QueryFields.Values['action'] = 'getnothing' then
    begin
      getNothing(Request, Response);
    end;

´   //Mehr Actionhandling..
  end;
end;

procedure TWebModuleMain.send501(text : String);
begin
  Response.StatusCode := 501;
  Response.ContentType := 'text/plain';
  Response.Content := text;
  Response.SendResponse;
end;

end.
Markus

Geändert von markusef (13. Jan 2017 um 13:59 Uhr)
  Mit Zitat antworten Zitat
nahpets
(Gast)

n/a Beiträge
 
#4

AW: WebModule & ADOConnection in Service -> Speicherübrelauf

  Alt 13. Jan 2017, 14:25
Meine letzten Webmodule sind schon 'ne Weile her, daher nur so aus dem Gedächtnis:

ADO-Komponenten habe ich auf das Webmodul gepappt. Sie werden beim Create des Webmoduls erstellt und beim Destroy wieder weggeräumt.

Habe sie also nicht bei jeder Anfrage an den Server erstellt und nach erledigter Arbeit freigegeben. Allerdings weiß ich nicht, inwieweit dieses mein Vorgehen threadsave ist.

Dein Vorgehen könnte da durchaus sinnvoller sein.

Wenn Du nur eine ADOQuery nutzt, benötigst Du keine ADOConnection, es reicht, wenn die ADOQuery den Connctionstring erhält.

Das CoInitialize(nil); und das CoUninitialize; stecken bei mir am Ende der Unit unter:
Delphi-Quellcode:
initialization
  CoInitialize(nil);

finalization
  CoUninitialize;

end.
Sie müssen nicht bei jeder Anfrage an den Server aufgerufen werden.

Ansonsten hab' ich an Deinem prinzipiellen Aufbau des Webmodules nix auszusetzen, allerdings solltest Du eventuell bei Methoden wie
Delphi-Quellcode:
procedure TWebModuleMain.tcmGet(Request: TWebRequest;
  Response: TWebResponse);
noch 'nen Try-Except-Block einfügen, damit Du bei Fehlern eine gescheite Fehlermeldung an den Client schicken kannst. Es muss sichergestellt sein, dass alles das, was Du in der Methode Send501 befüllst, immer gefüllt ist und (egal was passiert) am Ende ein erfolgreiches Response.SendResponse; steht. Ansonsten kann es passieren, das Webserver und/oder Client irgendwann ins Stolpern geraten. Und dann den Fehler suchen müssen, ist nicht wirklich spaßig.
  Mit Zitat antworten Zitat
markusef

Registriert seit: 9. Mai 2016
15 Beiträge
 
Delphi 10 Seattle Professional
 
#5

AW: WebModule & ADOConnection in Service -> Speicherübrelauf

  Alt 13. Jan 2017, 14:38
Okay, ja ein vernünftiges Exception Handling muss da definitiv noch rein, da hast du recht.
Das Daten abfragen mit der ADOQuery funktioniert auch so einwandfrei, Danke für den Tipp, das spart Quelltext.

Allerdings ist das Problem leider noch nicht behoben, mit jeder Anfrage steigt die Arbeitsspeicherauslastung des Services echt enorm, sinkt aber nach beendigung aller Anfragen auch nicht mehr

Woran kann das denn noch liegen ?
Könnte es sein das man die Webmodule nicht in einem Windows Service benutzen sollte ?

Habe mir da schon die Finger wund-gegooglet und überall werden die Webmodule nur in .dlls für IIS oder Apache genutzt

*EDIT:

CoInitialize(nil) & CoUni.. kann ich leider nicht in den jeweiligen Part für "initialization" und "finalization" packen, dann wirft mir Delphi vor ich hätte CoInitialize nicht aufgerufen :-/

*EDIT EDIT:

Grundlegende Frage: Wird, wenn sich ein Client mit dem Service verbindet, je Client ein WebModule Instanz erzeugt ? Oder pro Anfrage (auch vom selbem Client, weis ja nicht ob die Delphi Komponente sich das merkt) ?
Falls ja, wieso wird nachdem der RESTClient expliziert Disconnect aufruft die jweilige Instanz nicht abgeschossen ?
Markus

Geändert von markusef (13. Jan 2017 um 14:54 Uhr)
  Mit Zitat antworten Zitat
Jumpy

Registriert seit: 9. Dez 2010
Ort: Mönchengladbach
1.733 Beiträge
 
Delphi 6 Enterprise
 
#6

AW: WebModule & ADOConnection in Service -> Speicherübrelauf

  Alt 13. Jan 2017, 14:55
Hast du mal geschaut, ob vllt. bei jeder Request ein neues Webmodul erstellt wird? Wenn du das nicht so verwendest, wie es gedacht ist, könnte das doch sein?

OK, hast in deinem EditEdit ja die selbe Idee Edith

Vllt. zeigst du uns mal den Code des Services, wo das Webmodul eingebettet ist?
Ralph
  Mit Zitat antworten Zitat
nahpets
(Gast)

n/a Beiträge
 
#7

AW: WebModule & ADOConnection in Service -> Speicherübrelauf

  Alt 13. Jan 2017, 14:56
Meckert Delphi auch noch, wenn Du die beiden Co...Dingens ins Create bzw. Destroy packst?

Bei mir (mit Delphi 7) gab es von Delphi immer schimpfe, wenn ich es nicht bei "initialization" und "finalization" reingepackt hatte, sondern sonstwo.

Habe Webmodule nur in DLLs und CGIs für meinen Webserver genutzt. Ob das in 'nem Service nicht nutzbar ist, weiß ich nicht.
Aber eigentlich ist Dein Service doch das gleiche wie 'ne Exe plus DLL in "eins zusammengepackt"

Beim fünften mal Lesen Deines Quelltextes fiel mir was auf, weiß aber nicht, ob das Problem daher kommen könnte:

Auf ein ADOQuery.Open folgt kein ADOQuery.Close.
  Mit Zitat antworten Zitat
markusef

Registriert seit: 9. Mai 2016
15 Beiträge
 
Delphi 10 Seattle Professional
 
#8

AW: WebModule & ADOConnection in Service -> Speicherübrelauf

  Alt 13. Jan 2017, 15:07
Also anscheinend wird beim erstmaligen verbinden eine Instanz von TWebModule angelegt, danach (vorerst) keine weitere. Ich habe einen Testclient gebaut welcher eine Anfrage schickt, auf ein Response wartet und wenn ein Response ankam gleich den nächsten Request abschickt. Dann rutschte er zu Beginn zwei mal in die WebModuleCreate und anschließend lief alles im Alleingang (hatte beim Debugen nur einen Haltepunkt in der Create und in der Destroy), Destroy wurde auch erst bei Beendigung des kompletten Services ausgeführt.
Irgendwas ist da mächtig Faul

CoInitialize(nil) und CoUnitialize war auch mein nächster Tipp im Create, das funktioniert auch, komischerweise aber nur bis zu einem bestimmten Punkt, ab einer gewissen Anzahl von Anfragen wirft er mir plötzlich trotzdem eine Exception das CoInitialize nicht aufgerufen wurde.

ADOQuery.Close verhalf leider auch nicht zum gewünschten Erfolg :-/
Markus
  Mit Zitat antworten Zitat
Benutzerbild von p80286
p80286

Registriert seit: 28. Apr 2008
Ort: Stolberg (Rhl)
6.659 Beiträge
 
FreePascal / Lazarus
 
#9

AW: WebModule & ADOConnection in Service -> Speicherübrelauf

  Alt 13. Jan 2017, 15:37
Auf ein ADOQuery.Open folgt kein ADOQuery.Close.
Au backe das kann Mecker geben!
in meinen Anfangszeiten hab ich mir das Close solange geschenkt, bis das ich gemerkt habe, daß das der beste Weg zu ganz feinem Datenhack ist. Darum sehen meine Queries immer so aus:
Delphi-Quellcode:
myquery.close;
myquery.SQL.text='select Kontonummer from yourtable where userid=12345';
myquery.open;
{-- holen oder nicht holen ----------------------------}
myquery.close;
Damit bin ich bisher immer auf der sicheren Seite gewesen.

Gruß
K-H
Programme gehorchen nicht Deinen Absichten sondern Deinen Anweisungen
R.E.D retired error detector
  Mit Zitat antworten Zitat
nahpets
(Gast)

n/a Beiträge
 
#10

AW: WebModule & ADOConnection in Service -> Speicherübrelauf

  Alt 13. Jan 2017, 15:38
Das wird jetzt irgendwie etwas seltsam:

Jetzt kommt von mir mal nur vermuteter Spekulatius ohne Gewähr.

Meine mal gelesen zu haben, dass pro Client ein Webmodul erstellt wird. Also müsste pro Client auch einmal Create aufgerufen werden.

Bei der Verwendung von HTTP können (meine ich jedenfalls) zwischen einem Client und einem Server zwei Verbindungen aufgebaut werden. Dies würde das zweimalige Aufrufen von Create bei Nutzung eines Testclients erkären.

Was passiert, wenn Du den Testclient mehrfach startest. Erhöht sich die Anzahl der Create-Aufrufe dann?

Wenn ja, dann packe die ADOQuery mal auf das Webmodule, statt sie bei 'ner Anfrage selbst zu erstellen und wieder freizugeben.

Wird das Webmodule pro Client erstellt, dürfte es dann auch (kaum) zu Konflikten kommen, wenn mehrere Anfragen unterschiedlicher Clients zeitgleich beim Service ankommen.

Das mit dem CoIni-Dingens kann ich (nicht) verstehen.

Wenn das für die ganze Applikation gilt, dürfte bei wiederholten Anforderungen an den Service mit jeweiligen Aufrufen von CoInitialize und CoUninitialize und gleichzeitigen Anfragen mehrere Clients hier ein bisserl "Durcheinander" entstehen. Es ist ja nicht sichergestellt, dass die Anfragen vollständig nacheinander "abgearbeitet" werden.

Habe bei meinen ISAPI-Dlls erst dann Ruhe gehabt, nachdem ich die Aufrufe nach initialization bzw. finalization verschoben hatte.

Mal noch 'ne andere Idee:

Kannst Du die beiden Aufrufe nicht irgendwo außerhalb des Webmoduls im Service unterbringen?

Oder, nachdem ich mal in alte Quellen geguckt habe, in der DPR zu eine ISAPI-Dll steht dashier:
Delphi-Quellcode:
library WebSQLSearch;

uses
  ActiveX,
  ComObj,
  ISAPIThreadPool,
  ISAPIApp,
  BrkrConst in '..\..\..\Delphi7\Source\Internet\BrkrConst.pas',
  WebBroker in '..\..\..\Delphi7\Source\Internet\WebBroker.pas',
  WebSQLSearchUnit1 in 'WebSQLSearchUnit1.pas{wm: TWebModule};

{$R *.res}

exports
  GetExtensionVersion,
  HttpExtensionProc,
  TerminateExtension;

begin
  CoInitFlags := COINIT_MULTITHREADED; // <--- Hast Du das irgendwo untergebracht?
  Application.Initialize;
  Application.CreateForm(Twm, wm);
  Application.Run;
end.
Eventuell bekommst Du das Problem ja dann hiermit weg:
Delphi-Quellcode:
initialization
  CoInitFlags := COINIT_MULTITHREADED;
  CoInitialize(nil);

finalization
  CoUninitialize;

end.
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 1 von 3  1 23      

 

Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 22:39 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz