Discussion:
Anfragen an Sockets performant behandeln
(zu alt für eine Antwort)
Michael Gebhart
2003-12-08 09:45:33 UTC
Permalink
Hi zusammen,

ich habe ein Programm geschrieben, was Anfragen via Sockets entgegennimmt
und diese bearbeitet. In meinem Buch wurde das so gehandhabt, dass bei jeder
Anfrage ein neuer Thread gestartet wird, der dann die Anfrage bearbeitet,
damit weitere Anfragen angenommen werden können und die Anwendung bis zum
Ende der Bearbeitung nicht blockiert ist. Allerdings möchte ich mit diesem
Programm auch durchaus mal 30 oder sogar 50 Anfragen pro Sekunde bearbeiten
und da kommt es nun darauf an, dass die Anwendung insbesondere auch dieser
Teil performant sind. Ist die Technik, bei jeder Anfrage einen neuen Thread
zu starten denn die ideale Handhabung? Alternativ könnte man es ja so
machen, dass man 10 Threads zu Beginn startet, bei jeder Anfrage wird eine
Warteschlange um die neue Anfrage ergänzt, die Anwendung nimmt dann sofort
weitere Anfragen entgegen und die 10 Threads bearbeiten nacheinander die
Elemente in der Warteschlange. Allerdings müssen die Threads dann regelmäßig
die Warteschlange checken und wenn die 10 Threads mal zu langsam sind, dann
wird die Warteschlange immer länger und es kommt zu starken Verzögerungen.
Hat da jemand Erfahrungen, wie man sowas in Java oder generell performant
umsetzt?

Dankbar für Vorschläge

Michael
Jürgen Schlei
2003-12-08 09:51:44 UTC
Permalink
Hi Michael,

Apache macht es auch so, dass es für jeden Zugriff via Client einen Thread
abspaltet. Das Erzeugen von threads ist ähnlich wie allokieren und
zurückgeben von Speicher eine schnelle Operation.

Never mind.

Jürgen
Michael Gebhart
2003-12-08 10:26:47 UTC
Permalink
Das widerspricht der Aussage von Lothar aber jetzt gewaltig :)

MfG
Lothar Kimmeringer
2003-12-08 10:30:06 UTC
Permalink
Post by Michael Gebhart
Das widerspricht der Aussage von Lothar aber jetzt gewaltig :)
Juergen verwechselt Posix-Threads mit Java-Threads oder ist mir
die Java-Implementierung des Apache-Servers entgangen?


Gruesse, Lothar
--
Lothar Kimmeringer E-Mail: ***@kimmeringer.de
PGP-encrypted mails preferred (Key-ID: 0x8BC3CD81)

Always remember: The answer is forty-two, there can only be wrong
questions!
Jürgen Schlei
2003-12-08 11:28:17 UTC
Permalink
Hallo,

ACK. Threads sind tatsächlich aufwendig in Java.

Auf den ersten Blick könnte es so sein, dass Java als ein vom Betriebssystem
gekapseltes System eigentlich eine wesentlich schnellere Verarbeitung zuläßt
(kleine Spielwelt). Ist aber nicht so! Der Aufwand fällt vor allem beim
Erzeugen eines neuen Threads an.

Hab mir grade nochmal die Java Sources angeschaut...

Es lohnt sich also ein Array von z.B. 100 Threads (=maximale Anzahl
konkurrierender Zugriffe ) auf Vorrat anzulegen und als Slots
wiederzuverwenden.

merci bien Lothar
Lothar Kimmeringer
2003-12-08 10:24:54 UTC
Permalink
Am Mon, 8 Dec 2003 10:45:33 +0100 schrieb Michael Gebhart:

[Sockethandling schneller machen]

Da das Erzeugen von Threads (inkl. dem Verwalten innerhalb der
Threadgroups) sehr aufwendig ist, sollte man Threads wiederver-
wenden. Da ein einmal beendeter Thread nicht wieder gestartet
werden kann, kann man folgendermassen vorgehen:

In Deiner Klasse, in der der ServerSocket aufgemacht wird:

-----------------
ServerSocket ss = new ServerSocket(port);
for (int i = 0; i < THREAD_MAX; i++){
MyListenThread th = new MyListenThread(ss);
th.start();
}

// Was sonst noch so anfaellt

// Warten auf Applikationsende
-----------------

In MyListenThread:

public void run(){
while(notStopped){
Socket s = ss.accept();
// was auch immer mit dem Socket angestellt wird
}
}

ServerSocket.accept ist synchronized, daher gibt es kein
Problem und Du hast die Erzeugung des Threads aus dem
zeitkritischen Pfad herausgenommen.


Gruesse, Lothar
--
Lothar Kimmeringer E-Mail: ***@kimmeringer.de
PGP-encrypted mails preferred (Key-ID: 0x8BC3CD81)

Always remember: The answer is forty-two, there can only be wrong
questions!
Michael Gebhart
2003-12-08 10:28:46 UTC
Permalink
Hi,

na das finde ich ja interessant, dass ich auf ein und denselben Socket
mehrere accepts laufen lassen kann. Diese Methode gefällt mir sehr! Jürgen
im selben Thread scheint übrigens der Meinung zu sein, dass Threads zu
erzeugen sehr schnell geht!?

Ich wede das auf jeden Fall mal ausprobieren, danke!

MfG

Michael
Michael Gebhart
2003-12-08 23:12:15 UTC
Permalink
Hi,

habe das nun mal umgesetzt, aber noch keinen wirklichen
Geschwindigkeitsvorteil bemerkt. Das Problem: Zunächst funktioniert alles
prima, jedoch irgendwie hakt es nach ein paar Sekunden Powerflooden ab und
zu etwas und dann tut sich gar nix mehr. Es werden dann einfach keine
Anfragen mehr bearbeitet. Dann kommt wieder ein Schub, dann geht es weiter,
dann hakt es wieder und so weiter. Wie als wenn Java mittendrin sich
einfach mal ne Auszeit nimmt und nach ner kleinen Brotzeit weitermacht.
Sehr merkwürdig irgendwie, aber ich werde mal weitertesten :)

Mike
Robert Valentan
2003-12-11 19:26:27 UTC
Permalink
Post by Michael Gebhart
Hi,
habe das nun mal umgesetzt, aber noch keinen wirklichen
Geschwindigkeitsvorteil bemerkt. Das Problem: Zunächst funktioniert alles
prima, jedoch irgendwie hakt es nach ein paar Sekunden Powerflooden ab und
zu etwas und dann tut sich gar nix mehr. Es werden dann einfach keine
Anfragen mehr bearbeitet. Dann kommt wieder ein Schub, dann geht es
weiter, dann hakt es wieder und so weiter. Wie als wenn Java mittendrin
sich einfach mal ne Auszeit nimmt und nach ner kleinen Brotzeit
weitermacht. Sehr merkwürdig irgendwie, aber ich werde mal weitertesten :)
Das klingt nach Speicherbereiniung... Falls bei einer größeren Speicher-
zuteilung die Zeitspanne größer ist...

Ich verwende dazu Buffer-Pools, damit für den Datentransfer von und
zum Socket nicht ständig neue byte[] benötigt werden. Dies sollte das
Problem vermutlich lösen. Für andere Ideen ist der 'Input' zuwenig..
--
mfG
Robert Valentan
Max Pauer
2003-12-08 10:43:38 UTC
Permalink
Mein Tipp:

Vergiss Threads und befasse dich mit asynchron multiplexendem NIO.
Michael Gebhart
2003-12-08 11:00:18 UTC
Permalink
Aja,

noch nie gehört, aber werds mir mal anschauen, danke.

Michael
Ralf Brostedt
2003-12-08 19:04:04 UTC
Permalink
Post by Max Pauer
Vergiss Threads und befasse dich mit asynchron multiplexendem NIO.
Hast du ne Literatur-Quelle (am besten in deutscher Sprache) zu diesem Thema
parat?

Gruß,
Ralf
Boris Stumm
2003-12-09 08:06:19 UTC
Permalink
Post by Ralf Brostedt
Post by Max Pauer
Vergiss Threads und befasse dich mit asynchron multiplexendem NIO.
Hast du ne Literatur-Quelle (am besten in deutscher Sprache) zu diesem Thema
parat?
z.B.:

http://www-106.ibm.com/developerworks/edu/j-dw-java-nio-i.html
http://www.amazon.com/exec/obidos/tg
detail/-/0596002882/103-0380275-5611001?v=glance
http://www.owlmountain.com/tutorials/NonBlockingIo.htm

Auf java.sun.com müsste es auch noch was geben. Alles nicht auf deutsch, aber
besser als garnix ;)
Max Pauer
2003-12-09 08:04:47 UTC
Permalink
Post by Ralf Brostedt
Hast du ne Literatur-Quelle (am besten in deutscher Sprache) zu
diesem Thema parat?
Leider habe ich dazu in deutsch nichts gefunden. In Englisch
gibt es aber mehrere, relativ verständliche Tutorials:

u.a.:
http://www.onjava.com/pub/a/onjava/excerpt/javanut4_ch04/?page=3
http://www.onjava.com/pub/a/onjava/2002/10/02/javanio.html?page=4

Auch bei IBM kann man mal nach NIO suchen, da gibt es ein PDF,
dass die ganze Sache ziemlich kindgerecht zerlegt, leider kenne
ich die URL nicht mehr.

HTH
Michael Gebhart
2003-12-09 09:02:42 UTC
Permalink
Aber ist das dann nicht eher ein Ersatz für Sockets, bzw. den Streaming in
Sockets? Das ist doch keine Alternative zu Threads, oder wie soll ich NIOs
verstehen?

MfG

Michael
Max Pauer
2003-12-09 12:52:19 UTC
Permalink
Post by Michael Gebhart
Aber ist das dann nicht eher ein Ersatz für Sockets, bzw. den
Streaming in Sockets? Das ist doch keine Alternative zu Threads, oder
wie soll ich NIOs verstehen?
NIO heisst erst einmal nichts anderes, als New Input Output. Einfach
um das IO-Package des JDK davon zu unterscheiden.

Ausserdem hat damit endlich eine Technik Einzug gehalten, mit der man
richtig performante Serverlösungen (socket-basiert) realisieren kann.
Es gibt keine Input- oder Outputstreams mehr, sondern es wird mit sogenannten
Channels gearbeitet. Die näheren Details dazu verraten Dir die diversen
Tutorials. Asnychrones Multiplexing ist ebenfalls einer der neuen Aspekte.
Grob gesagt: Ein Channel wartet oder besser blockiert nicht bis man End-Of-Data
sendet, sondern er gibt die Kontrolle einfach zurück, wenn keine Daten
zu senden oder zu empfangen sind. Damit lassen sich hunderte Anfrage
in einem einzigen Thread beantworten, ohne das ein Anfrager warten müsste.

Ausserdem, ist das NIO sehr plattformnah implementiert. D.h. es werden
verstärkt die Besonderheiten des BS berücksichtigt und auch dessen spezielle
Funktionalitäten genutzt. Somit ergeben sich weitere Performance-Vorteile.

Am besten liest Du eines der genannten Tutorials und versuchst das Ganze
mal nachzuvollziehen. Dann wird Dir das Potenzial schnell bewusst, denke ich.
Lothar Kimmeringer
2003-12-09 13:46:19 UTC
Permalink
Post by Max Pauer
Asnychrones Multiplexing ist ebenfalls einer der neuen Aspekte.
Grob gesagt: Ein Channel wartet oder besser blockiert nicht bis man End-Of-Data
sendet, sondern er gibt die Kontrolle einfach zurück, wenn keine Daten
zu senden oder zu empfangen sind. Damit lassen sich hunderte Anfrage
in einem einzigen Thread beantworten, ohne das ein Anfrager warten müsste.
Ich habe mich mit NIO noch nicht so ganz beschaeftigt (bin zwar
im Serverbereich taetig, aber JDK 1.4 gibt es noch nicht auf allen
Plattformen, die ich braeuchte, daher kann ich es derzeit nicht
verwenden), aber soweit ich Dich verstehe, brauche ich doch dann
immer noch einen Thread pro Beantwortung, da doch sonst Requester
Nummer 100 auf die Bearbeitung der 99 vorigen warten muesste.

Oder habe ich da einen Fehler im Verstaendnis?


Gruesse, Lothar
--
Lothar Kimmeringer E-Mail: ***@kimmeringer.de
PGP-encrypted mails preferred (Key-ID: 0x8BC3CD81)

Always remember: The answer is forty-two, there can only be wrong
questions!
Max Pauer
2003-12-09 14:15:44 UTC
Permalink
Post by Lothar Kimmeringer
Ich habe mich mit NIO noch nicht so ganz beschaeftigt (bin zwar
im Serverbereich taetig, aber JDK 1.4 gibt es noch nicht auf allen
Plattformen, die ich braeuchte, daher kann ich es derzeit nicht
verwenden), aber soweit ich Dich verstehe, brauche ich doch dann
immer noch einen Thread pro Beantwortung, da doch sonst Requester
Nummer 100 auf die Bearbeitung der 99 vorigen warten muesste.
Oder habe ich da einen Fehler im Verstaendnis?
Du hast einen Fehler im Verständnis, wohl. ;)
/Einen/ Thread brauchst du natürlich immer noch. Der reicht allerings
für alles, was mit sog. SelectableChannels zu tun hat, aus.

Es gibt einen sog. Selector. An diesem registriert man die Channels
und erhält einen sog. Key. In einem Main-Loop, in dem der Selector
seine Channels überwacht, werden durch die Keys sozusagen Events
(Accept/Connect/Write/Read) kolportiert, wenn diese eintretten.

Nun liest Du bei einem Reading-Event die Daten in einen Buffer und
fertig. Dieser Teil darf nicht lange dauern, das ist richtig, aber
in den meisten Fällen ist der zu bestückende Buffer winzig klein
im Gegensatz zur Verfügung stehenden Performanz. Anders siehts
bei FileChannels aus. Diese kann man nicht in einen non-blocking-mode
schalten. Das verhält sich dann wie Streams ehedem, nur dass hier nichtmehr
zwischen In- und Output unterschieden wird. Ich kann hier aber nicht
alles erklären, dazu lohnt, wie ich schon sagte, auf jeden Fall ein
Blick in die Tutorials, vor allen Dingen sei an dieser Stelle das O'Reilly
Buch "...NIO" oder so empfohlen. Dort wird nochmal konkret auf die
Unterschiede zu dem alten Threading-Modell eingegangen, mit Vor- und Nachteilen
und so...
Michael Gebhart
2003-12-09 14:51:32 UTC
Permalink
Hi,

so ich habe mir das jetzt mal angeschaut und finde das durchaus sehr
interessant. Es erinnert stark an die Methode der Socketprogrammierung, wie
man sie unter C kennt. Allerdings habe ich den selben Denkfehler
anscheinend wie Lothar im Posting vor Dir *g* Und zwar ist es soweit ja
klar, dass ich ebene die Main-Loop habe, die immer überwacht was mit meinen
Sockets gerade passiert. Soweit alles wunderbar, ich hoffe mal, dass das
auch entsprechend performant ist, wenn da 1000 Sockets verwaltet werden
müssen. Jetzt ist nur: In dem Moment, wo ich merke: Ah ein Socket ist
bereit zum lesen, haben alle anderen Anfragen erstmal Pause. Wenn ich nun
mit dieser Anfrage eine beispielsweise ca. 2-3 Sekundenlange Operation
ausführen muss, dann haben alle anderen Anfragen 2-3 Sekunden keine Chance
entgegengenommen zu werden, weil ich mich ja immer noch in der Main-Loop
befinde. Ich müsste nun theoretisch vor dem Bearbeiten abbrechen und diese
längere Aufgabe nun irgendwelchen Threads überlassen, damit meine Main-Loop
zurückkehren kann, um weitere Anfragen wieder anzunehmen. Sicher blockiert
die Anwendung nun nicht mehr, solange sie auf Lese-oder Schreibsignale
wartet, aber wenn ich dann längere Operationen durchführen muss, dann
blockiert sie für diese Zeit alle anderen Anfragen. Also so gesehen brauche
ich ab diesem Punkt dann Threads, um die längeren Abwicklungen parallel
laufen zu lassen. Korrekt?

MfG

Michael
Max Pauer
2003-12-09 15:20:34 UTC
Permalink
Post by Michael Gebhart
Korrekt?
Korrekt!
Michael Gebhart
2003-12-09 16:33:52 UTC
Permalink
Ah super, danke schön!!

Michael
Juergen Kreileder
2003-12-12 05:23:34 UTC
Permalink
Post by Michael Gebhart
Aber ist das dann nicht eher ein Ersatz für Sockets, bzw. den
Streaming in Sockets? Das ist doch keine Alternative zu Threads,
oder wie soll ich NIOs verstehen?
NIO ist eine sehr gute Alternative, nur das Prinizip ist ziemlich
anders und etwas komplexer.

NIO bietet non-blocking I/O => man braucht nicht fuer jeden client
einen eigenen thread.
Non-blocking I/O kombiniert man meist mit multiplexing bzw. readiness
notifications. Mit NIO macht man das mit java.nio.channels.Selector:
Selector.select() sagt Dir, welche channels fuer I/O bereit sind.

Mit NIO reicht eigentlich ein einziger thread fuer alle clients aus.
Mit mehreren CPUs oder HyperThreading ist es aber durchaus sinnvoll,
die anfallende Arbeit auf mehrere threads zu verteilen.


Juergen
--
Juergen Kreileder, Blackdown Java-Linux Team
http://www.blackdown.org/java-linux/java2-status/
Loading...