# NativeIO und der Nutzen von Non-Blocking



## tuxedo (10. Aug 2007)

Hallo zusammen,

ich hab mich lange gegen NIO gewehrt weil's mir im Vergleich zur "herkömmlichen Technik" einen Socket-Server zu schreiben, zu komplex vor kam. Bzw ich schlicthweg zu faul war mir's im Detail anzusehen. Und bisher hab ich NIO auch noch nicht gebraucht.

Ich hab auch schon vor längerem gelesen dass NIO deutlich performanter und schneller sein soll als das normale IO Package. Deshalb hab ich mich nochmal hingesetzt und vesucht zu verstehen wie das funktioniert. Fazit: Is ja doch nicht so schwer 

Aber eins versteh ich noch nicht:

Wo liegt der tiefere Sinn der "non-blocking" Technik? Ich hab mir das Beispiel hier angesehen: http://javamagazin.de/itr/online_artikel/psecom,id,127,nodeid,11.html

Okay, da geht's um einen Single-Threaded-Server. Der kann nur einen Client nach dem anderen Bedienen. Da verstehe ich auch warum man non-blocking gebrauchen kann: Solange wie kein Client versucht zu connecten, solange kann ich in meinem einzelnen Thread was anderes machen, was sonst ja nicht ginge. Klingt logisch.

Aber nehmen wir jetzt mal einen Spiele-Server der mit mehreren Clients gleichzeitig kommunizieren muss. Da brauche ich eh' Threads wenn ich die Clients gleichzeitig mit Informationen versorgen will.
Heisst also: Jeder Client kommt in einen Thread. 

Aber wo liegt da jetzt der "Gewinn" des Non-Blockings? Wäre es nicht "praktischer" Dinge, die ich neben dem warten auf neue Clients noch machen will/muss in einem eigenen Thread zu behandeln? Vor allem stell ich mir's "kompliziert" vor, neben dem "non-blocking" ohne weiteren Thread, Dinge zu erledigen die selbst in eine While-Schleife gehören oder unter Umständen sehr viel Zeit benötigen. Soll der Client dann unter Umständen 'ne Minute warten bis er dran kommt oder wie? 

Wo habt ihr bis jetzt "non-blocking" gebrauchen können? Oder macht das wirklich nur Sinn wenn ich einen Single-Threaded-Server haben will oder muss?!

Vielleicht kann mich da jemand in die richtige Richtung weisen...

Gruß
Alex


----------



## tuxedo (10. Aug 2007)

Kommt schon, hat sich hier noch keiner Gedanken über sowas gemacht? Sonst seid ihr doch auch ziemlich fix im antworten ;-)


----------



## Guest (11. Aug 2007)

alex0801 hat gesagt.:
			
		

> Aber wo liegt da jetzt der "Gewinn" des Non-Blockings? Wäre es nicht "praktischer" Dinge, die ich neben dem warten auf neue Clients noch machen will/muss in einem eigenen Thread zu behandeln? Vor allem stell ich mir's "kompliziert" vor, neben dem "non-blocking" ohne weiteren Thread, Dinge zu erledigen die selbst in eine While-Schleife gehören oder unter Umständen sehr viel Zeit benötigen. Soll der Client dann unter Umständen 'ne Minute warten bis er dran kommt oder wie?
> 
> Wo habt ihr bis jetzt "non-blocking" gebrauchen können? Oder macht das wirklich nur Sinn wenn ich einen Single-Threaded-Server haben will oder muss?!


1. Neben dem warten, kannst du doch auch noch andere Threads laufen lassen. Ich hab einen Thread alleine fürs Akzeptieren der Verbindung und *3* für das Verarbeiten der informationen.

2. Die weiteren Threads kannst du dann ja meist auch per Laufzeit (also, wenn wirklich etwas zu verarbeiten ist) erstellen. 
Bsp: 1 empfangsthread erzeugt dynamisch Threads, wenn wirklich was ankommt
Ergebniss: 2 Threads laufen, 1. Thread kann wieder daten empfangen, Ressourcen vom 2. Thread werden wieder freigegeben, wenn alle Operationen beendet sind > Ressourcenverlust bei nio Minimal.

3. 
Möglichkeit 1:
Verteilung der Clients auf mehrere 'Einkommende Pakete-Threads' 

Möglichkeit 2:
Erzeugen von dynamischen Threads - je nach Ereigniss

...

4. Wo? Chat

5. Sinn? Prinzipiell kann man es überall einsetzen


----------



## tuxedo (11. Aug 2007)

Anonymous hat gesagt.:
			
		

> 1. Neben dem warten, kannst du doch auch noch andere Threads laufen lassen. Ich hab einen Thread alleine fürs Akzeptieren der Verbindung und *3* für das Verarbeiten der informationen.


Wie? Neben dem warten? Beim "non-blocking" muss man nicht warten. Schließlich "blockt" da ja nix. Wenn du "non-blocking" benutzt: Was machst du in der Endlosschleife in der du immer wieder schaust: "Sind neue Clients da?"

Ich hab so das gefühl dass du "Blocking" verwendet hast. Weil meine Frage war ja:

Wo macht das "non-blocking" Sinn wenn ich den gleichen/ähnlichen Effekt mit "blocking" + weitere Verarbeitungs-Threads habe?


- Alex


----------



## tuxedo (13. Aug 2007)

Leute, was ist los? Thema "zu langweilig" oder "unbekannt" ?


----------



## planetenkiller (13. Aug 2007)

alex0801 hat gesagt.:
			
		

> Anonymous hat gesagt.:
> 
> 
> 
> ...



Ich mache das genau so wie der Gast, ich habe einen Thread der nur Verbindungen akzeptiert. Und n Threads für das verarbeiten. Natürlich alles mit NIO und Selectoren.

Ich glaube du hast NIO nicht ganz verstanden. Der Selector.select() blockiert solange, bis eine oder mehrere I/O Operationen anstehen. 

Sinn....Was denkst du ist besser:
- Für Tausende Clients je einen Thread zu haben oder
- Vielleicht 10 Selector-Threads die alles abarbeiten
?



> Leute, was ist los? Thema "zu langweilig" oder "unbekannt" ?


Ich würde sagen "unbekannt". NIO ist recht komplex, die meisten werden auf die normale IO setzen.


----------



## tuxedo (13. Aug 2007)

Hast du dir den Link angesehen den ich gepostet hatte?
Hier nochmal der Code, etwas "aufgeräumt" und mit meinen Kommentaren für mein besseres verständnis versehen:

Server:

```
import java.net.*;

import java.util.*;

import java.nio.*;

import java.nio.channels.*;

// a simple single threaded echo server with java.nio in java 1.4

public class ServerNew {

	private long workTime = 10000;
	private Selector readSelector;
	private ByteBuffer buffer = ByteBuffer.allocate(1);

	
	public void start() throws Exception {

		System.out.println("ServerNew started!");

		SocketChannel client;

		readSelector = Selector.open();

		ServerSocketChannel channel = ServerSocketChannel.open();
		ServerSocket socket = channel.socket();
		socket.bind(new InetSocketAddress("localhost", 8777));

		channel.configureBlocking(false); // Socket auf non-blocking einstellen

		while (true) {

			client = channel.accept(); // Mal einen Blick auf den Socket werfen und schauen ob neue Clients da sind -> blockiert nicht
			doSomeWork(); // irgendwas andere noch schnell erledigen

			if (client != null) { // So, und hier auswerten ob wirklich Clients "vor der Tür" standen

				client.configureBlocking(false); // Weil jeder Channel per "default" blockt, muss das erst umgestellt werden.
				client.register(readSelector, SelectionKey.OP_READ); // Den Channel im Selektor (Container für die anfragenden Clients) registrieren

			}

			handleClientRequest();

		}

	}

	public void handleClientRequest() throws Exception {

		Set keys;
		Iterator it;
		SelectionKey key;
		SocketChannel client;

		// Solange im Container noch Channels drin sind ...
		if (readSelector.selectNow() > 0) {

			// Die Schlüssel der Channel holen
			keys = readSelector.selectedKeys();
			// und einen Iterator drauf anwenden, damit wir durch die Keys durchlaufen können
			it = keys.iterator();

			// Solange im Iterator noch Einträge vorhanden sind -> durchlaufen
			while (it.hasNext()) {

				// Den Schlüssel des Channels aus dem Iterator holen
				key = (SelectionKey) it.next();

				// Wenn der Client das lesen erlaubt ...
				if (key.isReadable()) {

					// Client holen
					client = (SocketChannel) key.channel();

					buffer.clear();
					client.read(buffer);
					System.out.println("'" + buffer.get(0) + "' Client received!");
					buffer.flip();

					client.write(buffer);
					client.close();

				}

			}

		}

	}

	public void doSomeWork() throws Exception {

		// Simulates a processing time of about a 1/10 Second

		if (workTime > 0)

			Thread.sleep(100);

		workTime -= 100;

	}

	public static final void main(String args[]) throws Exception {

		(new ServerNew()).start();

	}

}
```

Client

```
import java.net.*;

// simulates 100 client-request every 1/4 second and takes the time

public class Clients {



public void sendRequests (int numberOfClients, int delayBetweenRequests) throws Exception {

for (int i = 0; i < numberOfClients; i++) {

Thread.sleep(delayBetweenRequests);

Socket clientSocket = new Socket("localhost",8777);

clientSocket.getOutputStream().write(i);

System.out.print("Client Nr. "+i+" has sent");

System.out.println(" and receive: "+clientSocket.getInputStream().read());

clientSocket.close();

}

}



public static void main(String[] args) throws Exception{

long startTime = System.currentTimeMillis();

(new Clients()).sendRequests(100,250);

System.out.println("Time: "+((System.currentTimeMillis()-startTime)));

}

}
```

Da kommt kein Selector.select() drin vor. Und da blockiert auch nix, wirklich gar nix. 

Für 100 Clients, 100 Threads zu bastelnm ist ja kein Ding. Ich mein es ist nicht besonders aufwendig, höchstens cpu und ram lastig.

Ich versteh jetzt nicht ganz wie ich in einem Thread 10 Clients gleichzeitig "bedienen" will. Die kann ich doch, wenn dann nur sequentiell bedienen (okay, ich weiß, Threads arbeiten auch mittels Zeitschlitzverfahren sequentiell, aber das ist auf nem anderen niveau). 

Hast du nen Link zu nem Beispiel mit dem Verarbeiten der Clients in einem "Selector-Thread" ? Kann mir das irgendwie nicht vorstellen.

- Alex


----------



## planetenkiller (13. Aug 2007)

Zum Beispiel unter http://www.exampledepot.com/egs/java.nio/NbServer.html, ungefähr so habe ich es auch.
Auch das ist gut: http://homepages.fh-giessen.de/~hg51/Verteilt/verteilt-03.pdf



> Für 100 Clients, 100 Threads zu bastelnm ist ja kein Ding. Ich mein es ist nicht besonders aufwendig, höchstens cpu und ram lastig.


Für 100 Clients kein Problem, würde ich auch mit Threads machen.





> Ich versteh jetzt nicht ganz wie ich in einem Thread 10 Clients gleichzeitig "bedienen" will


Gleichzeitig nicht, es ist so: Der Selector verwaltet zb. 10 Clients, wenn ein(oder mehrere) Client(s) bereit ist(sind) Daten zu senden, dann kehrt die Methode selector.select() zurück, und man kann eine liste mit allen bereiten Clients holen und dann mit einer for/while schleife alle Events abarbeiten.  Danach blockiert das selector.select() wieder solange, bis wieder Events(Bereit zum senden, neue Cleints oder Bereit zum Entpfangen) anstehen.


Du kannst ja mal meine Dateien anschauen:

ServerController kontrolliert den Acceptor und die Readers
ServerConnectionAccept(Acceptor) Akzeptiert neue Verbindungen, werden bei einem Reader registriert
ServerConnection(Reader) Behandelt alle hereinkommenden Daten und Sendet evtl. vorhandene Daten zurück


----------



## tuxedo (13. Aug 2007)

Ah, jetzt "schnall" ich's... D.h. ich hab hab beispielsweise 10 Clients pro Verarbeitungsthread. Und da warte ich "blockierend" auf eingehende Daten die ich dann verarbeiten lasse, wenn sie denn anstehen ... 

Okay, ich schau mir deine Links und Files mal genauer an. Danke mal soweit. Ich komm auf dich zurück wenn ich nich fragen hab 

- Alex


----------



## Franz Teuner (23. Aug 2007)

Unter Java.Net gibt es hierzu einen Artikel (in englisch)l: http://today.java.net/cs/user/print/a/350 

- Franz


----------



## Dante (23. Aug 2007)

das problem ist: threads erzeugen overhead, z.B. beim wechseln. Zwar nicht so viel wie prozesse es tun (ein prozesswechsel benötigt zigtausende cpu-zyklen).

daher hast du zB. bei großen webserver nur zwei astellige zahlen von prozessen und die bearbeiten dann alle anfragen. So kannst du zB. mehrere hundert offene verbindungen haben, aber nur dort ressourcen verbrauchen wo etwas los ist. 

Meistens gibt es in den betriebssystem auch bessere io-scheduler für solche sachen.

ob eine vm mit 100 aktiven threads noch performant ist weiss ich nicht genau, irgendwann ist auf jedenfall die grenze da. und wenn du  einen dienst anbietest, wo verbindungen auch mal länger rumliegen, ist es doch unsinnig dafür immer einen thread zu verschwenden.

für sowas könnte man ja auch mal ganz einfache messungen schreiben, einfach nen kleinen http-server und dann mal ein paar hundert anfragen auf machen. da kann man dann beide modelle schön vergleichen.


----------



## tuxedo (28. Aug 2007)

Also den Nutzen von NIO ab ich jetzt geschnallt. Maßgeblich geholfen hat der Artikel den Franz verlinkt hat. Danke hierfür.

Die Sache mit dem EventHandler leuchtet ein. Und da leuchtet's auch ein warum ich nicht mehr pro Client einen Thread brauche. Und zwischen all dem, taucht auch noch das non-blocking auf. 

Und da lag für mich der Hund begraben: Non-Blocking alleine ist es nicht was NIO so viel besser macht wie das normale IO. Es sind die ganzen Sachen drum rum die da ebenfalls eine (gewaltige) Rolle spielen.

*Problem abhak'*

- Alex


----------

