Gå til innhold

Spørsmål om tråder og nettverk i java


Anbefalte innlegg

Hei,

 

driver for øyeblikket å programmerer en server i java. Enn så lenge så skal den kun sende strings over nettverk med TCP.

 

Serveren har en maintråd som kjører en kommandoløkke, og når serveren startes lager den en ny tråd for å håndtere nye klienter.

 

Når en klient har koblet til, får den sin egen tråd. Så om 3 klienter har koblet til, skal det totalt være 5 tråder (main, listener, 3x client).

 

Problemet mitt er at med en gang jeg kobler opp 2 klienter begynner laptoppen min å jobbe alt for mye (opptil 25% cpu til java.exe) Når jeg har 3-4 topper den til 85%.

 

Jeg har virkelig ikke peiling på hvordan programmet suger så mye cpu så fort, men jeg lurer på om ikke det er på grunn av nettverkskoden? (Se nedenfor)

 

Når jeg starter opp serveren så har jeg oppe resourcemanager samtidig, og finne java.exe som blir startet. Allerede da står antall tråder til 19? Er dette riktig er har jeg gjort noe merklig? Jeg antar at det bare er slik.

 

Lyttertråden:

public void run() {
	if (debug) {
		System.out.println("#D# Listener stats:");
		System.out.println("# port: " + port);
	}
	// Try to connect socket to port
	try {
		if (debug) {
			System.out.println("#D# Trying to start listening on port: " + port);
		}
		serverSocket = new ServerSocket(port);
	} catch (Exception e) {
		System.out.println("Could not create socket.");
		if (debug) {
			System.out.println("#D# Error:\n" + e);
		}
		return;
	}
	System.out.println("Successfully started listening on port " + port);
	try {
		while (true) {
			Socket clientSocket = serverSocket.accept();
			System.out.println("New connection.");
			if (debug) {
				System.out.println("#D# IP: " + clientSocket.getInetAddress());
			}
			Client client = new Client(clientSocket, debug);
			// clientData.add(client);TODO
			client.start();
		}
	} catch (Exception e) {
		System.out.println("Could not etablish connection.");
		if (debug) {
			System.out.println("#D# Error:\n" + e);
		}
	}
}

 

Klient-koden:

public void run() {
	String input;
	while (socket.isConnected()) {
		try {
			input = in.readLine();
			if (input != null) {
				System.out.println(input);
			}
		} catch (Exception e) {
			System.out.println("Problem reading inputstream");
			if (debug) {
				System.out.println("#D# Client IP: " + socket.getInetAddress());
				System.out.println("#D# ThreadID: " + this.toString());
				System.out.println("#D# Error: " + e);
			}

			break;
		}
	}
	try {
		in.close();
		socket.close();
	} catch (Exception e) {
		System.out.println("Problem closing connection.");
		if (debug) {
			System.out.println("#D# Client IP: " + socket.getInetAddress());
			System.out.println("#D# ThreadID: " + this.toString());
			System.out.println("#D# Error: " + e);
		}
	}
}

 

når jeg sier klient-koden så er det koden fra run metoden til trådene som tar seg av kommunikasjonen med klienten.

 

 

Jeg har lest litt om OIO og NIO server-klien arkitekturer, og her snakkes det om 3-400 tråder før java begynner å skape problemer.

 

Hardware på laptop (Om det skulle være relevant):

i7-2670QM 2.20 GHz

6 GB ram

 

 

Når det gjelder selve klienten som sender strings, så er den koden så liten og utgjør ingenting, men for sikkerhets skyld legger jeg ved den og:


	try {
		Socket clientSocket = null;
		DataOutputStream os = null;
		clientSocket = new Socket("127.0.0.1", 13337);
		if(clientSocket.isConnected())
			System.out.println("Connected!");

		clientSocket.setTcpNoDelay(true);


		String input = "";
		os = new DataOutputStream(clientSocket.getOutputStream());

		while(!input.equals("exit")) {
			input = in.nextLine();
			os.writeBytes(input + "\n");
			os.flush();
		}

			os.close();
		clientSocket.close();
	} catch (Exception e) {
		System.out.println(e);
	}

 

Alt fungerer med unntak at cpu blir sugd ut av maskinen! :)

Lenke til kommentar
Videoannonse
Annonse

Fant en feil som gjorde en STOR forskjell.

 

Jeg misforsto hvordan nettverksstrømmen fungerte og hoppet over alt som returnerte "null", når jeg egentlig skal break; når strømmen returnerer null.

 

riktig kode blir (for de intresserte):

			if ((input = in.readLine()) != null) {
				System.out.println(input);
			} else {
				System.out.println("Connection disconnected");
				break;
			}

 

Om noen har andre tips eller ser noen feil er alle tilbakemeldinger godt mottatt! :)

Endret av Diablonor
Lenke til kommentar
  • 3 uker senere...

Noen få tips, jeg jobber nemlig på en Java server for tiden selv i forbindelse med jobb:

 

1. Jvisualvm, det er et verktøy som følger med JDK og som er utrolig nyttig for å drive med profilering, sampling, se ressursbruk osv. i servere.

2. JRebel, få tak i gratisversjonen av det og eksprementer med det verktøyet, det er ufattelig nyttig, basicly så er det en veldig bra hot-swapping løsning for Java koding. Jeg gjør endringer i server koden, legger til nye funksjoner, statiske variabler, metoder, etc. så fort jeg trykker save så vil klassen bli oppdatert automagisk (ja, magisk, det funker i de fleste tilfeller!).

3. Når du føler for å utvide serveren din for å takle flere klienter, sjekk ut Netty.io, det er et svært bra NIO server rammeverk. Når jeg hadde funksjoner som ikke krevde stort så lå jeg på 2% CPU og 500MB ram med 25k tilkoblede klienter. Nå ligger jeg på 20-40% og 1GB ram med like mange klienter, og da driver jeg med mye database behandling og to-veis kommunikasjon, kan helt sikkert optimalisere betraktlig da jeg har på veldig mye logging akkurat nå som spiser opp CPU (det har jeg sett ganske enkelt via JVisualVM :) )

Endret av TheRealL
Lenke til kommentar
  • 2 uker senere...

Teorier:

 

Du konsumerer data som blir sendt til server, right? Faktum er at socket-rammeverket er bygd opp slik at om den andre enden av socketen ikke tar imot dataene, sendes de på nytt ad infinitum, noe som kan gjøre at Heap fylles med data som ikke blir mottatt, men som er "in-transit". Grunnen til at jeg spør er fordi jeg ikke ser noen DataInputStream.

 

Det kan hende at Streamen du bruker oppfører seg som ObjectOutputStream som jeg er mest kjent med. Denne cacher all data som sendes, og om samme objekt sendes flere ganger, sender den bare en referanse, og ikke hele objektet på nytt, slik sparer Java båndbredde, men det er ingen grense for hvor stor Cachen blir, som fører til minnelekkasje. Derfor; Prøv å kjøre out.reset() en gang i blant (hver 1000. forsendelse eller noe).

 

Begge disse er RAM-problemer. Når Java heapen blir veldig stor, begynner javas Garbage-Collector å kjøre mer og mer aggressivt, som fører til høy CPU bruk.

 

Angående trådene: Java lager et titalls tråder for internt bruk. Ikke noe å bekymre seg over.

 

Til slutt: Jeg har gode erfaringer med jconsole som følger med JDK. Kjør det og se hvilken tråd som sluker CPU, og om du har abnorm RAM-bruk.

 

EDIT: Prøv å kjøre bare nettverkskoden. Bygg deg en liten dummy-klasse som rigger en server og en klient og sender et par tusen strenger gjennom, bare for å være sikker.

Endret av Mads-b
Lenke til kommentar

Opprett en konto eller logg inn for å kommentere

Du må være et medlem for å kunne skrive en kommentar

Opprett konto

Det er enkelt å melde seg inn for å starte en ny konto!

Start en konto

Logg inn

Har du allerede en konto? Logg inn her.

Logg inn nå
×
×
  • Opprett ny...