& PHP Wieder mal eine harte Nuss ;-)
uepselon
- java
Hallo,
ich bin beim erstellen eines "kleinen" Java Programms mal wieder auf ein paar kleine Schwierigkeiten gestoßen.
Ursprünglich wollte ich von einem Java Programm aus (lokal), über ein php Script (im Internet verfügbar) von beliebigen Sourcen, Daten downloaden. Hierzu ein kleines Schema, damit man mich hoffe ich auch versteht:
Java Programm (schickt Anfrage, z.B. "http://einserver/file.zip) -> http://phpserver/proxyscript.php (nimmt Anfrage entgegen, ließt von file.zip und gibt die Daten in 10 KB schritten an das Java Prog.)
Soweit so gut, nun die Probleme, ich kann zwar mit dem PHP Script die Datei an Java übergeben, da aber das ganze nicht am Stück geschieht sonder in 10 KB Paketen, muss das PHP Script, die Datei (file.zip) bei jedem neuen Zugriff an der aktuellen Stelle öffnen. Sprich es soll nicht immer von Beginn an gelsen werden, sondern erst von der Stelle wo das letzte Paket beendet wurde.
Bsp.: Zu Beginn ist der Startpunkt 0, dann 10240, dann 2*10240 ...
Das hatte ich mit fseek(Startpunkt) in PHP realisiert. Das dumme ist nur, das bei Filehandles die mit http:// beginnen, fseek nicht funktioniert. Könnte man das nicht anders lösen?
Desweiteren gibt es ein massives perfomance Problem, für eine poplige 200 KB Datei, die zu testzwecken vom lokalen Webserver geladen wurde, benötigt man schon ca. 10-20 sec. liegt evtl daran das ich alles Byteweise lesen und schreiben muss, da eine Zeilenweise übermittlung, bei Binärdaten wie zip Files etc. Schwachsinnig ist. Hab das Ganze auch mal direkt versucht, also Java Prog. -> Downloadquelle, war aber auch nicht schneller.
Wie könnte ich hier mehr perfomance herausholen?
Anbei, zwei kleine SourceCodes:
1. Java Code (direkter download, aber von der Methodik des InputStreams etc. äquivalent zur php variante)
URL url = new URL("http://localhost/file.zip");
InputStream is = url.openStream();
is.skip(fsize);
int rbytes = fsize;
int cbuf;
while ((cbuf = is.read()) != -1) {
out_file.write(cbuf);
}
2. PHP Code ($para, übergibt den Stratpunkt oder den Befehl "getSize" um die komplette größe der Datei zu ermitteln)
$buffer = "";
$fp = fopen("file.zip","r");
$cnt=-1;
$size = filesize("file.zip");
$start = $para;
if ($para != "getSize")
{
fseek($fp, $start);
while(($cnt < 10240) && (!feof($fp)))
{
$cnt++;
$buffer.=fgetc($fp);
}
echo ($buffer);
}
else
echo $size;
fclose($fp);
Über Verbesserungsvorschläge würde ich mich freuen ;-)
Gruß
ueps
Moin!
Java Programm (schickt Anfrage, z.B. "http://einserver/file.zip) -> http://phpserver/proxyscript.php (nimmt Anfrage entgegen, ließt von file.zip und gibt die Daten in 10 KB schritten an das Java Prog.)
Die Frage, die uns alle bewegt: Warum zum Teufel in 10KB-Schritten? Ohne diese Anforderung wäre dein Problem keines - und schätzungsweise denkst du zu kompliziert für deine Lösung. Ich würde nämlich, wenn das Java-Programm denn unbedingt 10KB-Happen haben will, einfach 10KB-weise vom TCP-Socket einlesen - das PHP-Skript selbst merkt von der Paketierung nichts, sondern liefert einen Datenstrom aus - ganz schlicht und einfach. Wenn der Java-Client nicht weiterliest, um z.B. Daten zu verarbeiten, gerät halt der Datenstrom ins Stocken. Ist zwar nicht unbedingt schön, weil während der Übertragung natürlich Serverressourcen gebunden sind - aber während eines langen Downloads mit einem normalen Browser per Modem ist das ja auch der Fall - und selbst wenn der Client schneller möchte, gehts wegen der Leitung nicht. In deinem Fall gehts wegen dem Client eben nicht.
Desweiteren gibt es ein massives perfomance Problem, für eine poplige 200 KB Datei, die zu testzwecken vom lokalen Webserver geladen wurde, benötigt man schon ca. 10-20 sec. liegt evtl daran das ich alles Byteweise lesen und schreiben muss, da eine Zeilenweise übermittlung, bei Binärdaten wie zip Files etc. Schwachsinnig ist. Hab das Ganze auch mal direkt versucht, also Java Prog. -> Downloadquelle, war aber auch nicht schneller.
Wenn du jedes 10KB-Stück als separaten HTTP-Request anforderst, dann ist deine Performance wirklich kein Wunder. Bedenke, dass jedesmal eine TCP-Verbindung hergestellt werden muss, dann der Server das PHP-Skript startet, dann PHP seinerseits eine HTTP-Verbindung herstellen muß, Daten liest, puffert und weiterleitet, und schließlich dein Java-Programm die Daten erhält. Massiv umständlich, würde ich mal sagen. Insbesondere die ständigen Verbindungsherstellungen kosten Zeit. Daumenregel: Eine HTTP-Verbindung liefert frühestens nach der doppelten Ping-Zeit zum Zielrechner erste Daten.
Überleg mal selbst: Gute Ping-Zeiten liegen beispielsweise bei 50 ms - also fließen nach frühestens 100 ms Daten. Das ganze aber doppelt (das PHP-Skript benötigt ja auch Zeit zum Verbindungsaufbau). Also pro Element schon mal mindestens 200 ms. Deine Testdatei zerfällt in 10 Stücke - sind schlappe 2 Sekunden nur für Verbindungsaufbauten, wo normalerweise ein Zehntel der Zeit reichen würde.
Was ebenfalls Zeit kostet, sind die Serverreaktionen. Nimm einfach einen Wert an: Wie lange benötigt ein Server vom Eingang des Requests bis zur Auslieferung des Content? Multipliziere für dein Stückendownload mit zehn.
Dann kommt natürlich die Bandbreite der beteiligten Leitungen ins Spiel. Es ist schwer abzuschätzen, was du zur Verfügung hast - und außerdem weiß ich nicht, wie lange der Download der Datei normal dauert - aber da du die Leitung zusätzlich mit HTTP-Requests und Responses belegst, die zwischen 500 Byte und 1 Kilobyte groß sein können, verlierst du auch dadurch zehnmal mehr Performance.
Summa summarum ist wohl davon auszugehen, dass du 5 bis zehnmal mehr Zeit zum Download benötigst, als wenn du ohne Flickenteppich downlädst.
Da du in Java ja ohnehin einen Stream liest - lies einfach solange, wie du Daten benötigst, und mache Pausen, wenn du gerade keine Daten benötigst - die Daten laufen dir nicht weg, sondern stehen in Puffern, die nicht überlaufen, weil einfach keine Daten nachgeschickt werden, wenn der TCP/IP-Netzwerklayer deines Rechners keine Bestätigung schickt, dass wieder Platz im Puffer ist.
- Sven Rautenberg
Hi,
Die Frage, die uns alle bewegt: Warum zum Teufel in 10KB-Schritten? Ohne diese Anforderung wäre dein Problem keines - und schätzungsweise denkst du zu kompliziert für deine Lösung.
Es muss oder besser sollte eben in 10 KB oder von mir aus auch 100 KB Schritten erfolgen. Sinn ist es eine Trafficbeschränkung für einzelne Dateien, ein bischen zu umgehen in dem eben nur kleine Pakete geladen werden. Ich habe zu testzwecken auch einmal versucht, alles direkt zu empfangen ohne einzelne Pakete. Hat aber bei der besagten 168 KB Datei, genausolange gedauert. Und das ganze lief bei mir lokal wo Java Prog und Webserver mit der Testdatei auf einem Rechner laufen. Könnte es sein, das es in Java viel Zeit kostet InputStreams und FileStreams jedesmal neu zu deklarieren?
Was mich jedoch etwas mehr interssieren würde, waäre wie man in PHP etwas wie fseek() auch auf URL FileHHandles anwenden kann?
Gruß
ueps
Hi,
Die Frage, die uns alle bewegt: Warum zum Teufel in 10KB-Schritten? Ohne diese Anforderung wäre dein Problem keines - und schätzungsweise denkst du zu kompliziert für deine Lösung.
Es muss oder besser sollte eben in 10 KB oder von mir aus auch 100 KB Schritten erfolgen. Sinn ist es eine Trafficbeschränkung für einzelne Dateien, ein bischen zu umgehen in dem eben nur kleine Pakete geladen werden.
Ich verstehe den Sinn immer noch nicht. Würdest du etwas mehr ins Detail gehen - oder würde das dein Tun als illegal demaskieren? :)
Ich habe zu testzwecken auch einmal versucht, alles direkt zu empfangen ohne einzelne Pakete. Hat aber bei der besagten 168 KB Datei, genausolange gedauert. Und das ganze lief bei mir lokal wo Java Prog und Webserver mit der Testdatei auf einem Rechner laufen. Könnte es sein, das es in Java viel Zeit kostet InputStreams und FileStreams jedesmal neu zu deklarieren?
Ich habe keine Ahnung von Java.
Was mich jedoch etwas mehr interssieren würde, waäre wie man in PHP etwas wie fseek() auch auf URL FileHHandles anwenden kann?
Nein, das geht aus guten Gründen nicht: Wenn man mitten in die Datei "greifen" will, muss man sie komplett vorliegen haben. Auf Festplatte liegt sie komplett vor, der Zugriff auf die Mitte oder das Ende ist problemlos. Im Netz besteht das Problem, dass ein HTTP-Datenstrom immer von vorne an gelesen werden muss, um bis zur Mitte zu gelangen. Danach ist aber kein Zurückspringen weiter nach vorn mehr möglich etc.
Du hast zwei Möglichkeiten: Entweder du lädst mit PHP die HTTP-Ressource komplett herunter und speicherst sie in einer temporären Datei auf der Festplatte - dann kannst du mit fseek() arbeiten. Oder du lernst "HTTP" zu sprechen und beschäftigst dich mit den Möglichkeiten, vom Webserver Teilbereiche von Ressourcen abzufragen (auf diese Weise arbeiten z.B. Download-Manager, indem sie die große Datei in einzelnen kleineren Stücken abfragen und parallel runterladen - bringt wohl tatsächlich kleine Geschwindigkeitsvorteile). Dazu müsstest du aber mit fsockopen() eine TCP-Verbindung zum Webserver herstellen - und auf seine Reaktion entsprechend reagieren, denn der Server muß nicht zwingend mit Statuscode 206 Partial Content antworten (das wäre eine gute Antwort für dich, weil dann der gewünschte Dateiausschnitt kommt).
- Sven Rautenberg