PDA

View Full Version : [Java] Come migliorare le prestazioni di una complessa Applet?


UnknownSoldier
28-08-2008, 11:26
Salve. Da circa un mese a questa parte sto lavorando assiduamente alla creazione di una chat utilizzando applet dal lato client e uno script php dal lato server. Ho ancora parecchie cose da sistemare, ma la cosa principale sono le prestazioni. Se avvio l'applet da locale, il programma scorre una meraviglia, ma se l'avvio on-line noto dei rallentamenti. Sarà perchè il collegamento con i file (sì uso diversi file di testo per gestire conversazioni, registrazioni ecc...) nella rete è indubbiamente più lento del locale, ok. Ma io noto che il programma mi va a scatti anche solo se digito del testo in un JTextField, però non sempre. Ci sono delle volte in cui non ci sono rallentamenti, e certe volte in cui sì. In locale invece non ho mai avuto rallentamenti di alcun tipo... non mi metto di certo a postare l'intero sorgente, dato che sono più di 2900 righe di codice.

Vorrei sapere in linea generale gli elementi che più influiscono sulle prestazioni. Ad esempio, attualmente uso tre Timer, ognuno agisce in tempi separati. Se sostituissi i Timer con dei Thread sarebbe meglio? E cosa cambia tra Applet e JApplet?
Insomma, vorrei dei consigli per migliorare le prestazioni della mia applet...

Per provare la chat (ovviamente ancora incompleta), potete andare a questo indirizzo: http://www.lenostreidee.net/DFChat/DFChat.html

Magari mi dite se anche voi notate dei rallentamenti o no... avendo un solo computer mi risulta difficile fare test on-line ;)

Ciao e grazie per l'attenzione

marko.fatto
28-08-2008, 12:11
mi va senza problemi.. non noto rallentamenti ;)

variabilepippo
28-08-2008, 12:17
Nessun rallentamento apprezzabile...

ndakota
28-08-2008, 12:21
io ho notato solo che se mandi 4/5 messaggi velocemente tipo di una sola lettera appaiono tutti insieme, ma penso sia normale..

banryu79
28-08-2008, 12:55
Provata anch'io assieme un altro utente e sia io che l'altro abbiamo notato qualche lag (non si verificava sempre però) sia durante la digitazione del testo nel JTextField sia nell'invio del messaggio e la sua effettiva apparizione nella finestra di chat.

Puoi provare a spiegarci in che modo usi i Timer e per controllare cosa?
Inoltre quante letture/scritture dai/sui file locali fa la chat?
Magari si può elaborare una strategia per bufferizzare e scrivere sui file solo quando strettamete neccessario, o nei "tempi morti".

Ciao :)

P.S.: io avevo anche qualche problema di charset credo, perchè le vocali accentate spedite dall'altro utente non le vedevo visualizzate correttamente nella finestra di chat, mentre se ero io a spedirle alla chat, l'altro utente le vedeva correttamente.

UnknownSoldier
28-08-2008, 13:09
Innanzitutto grazie per l'interessamento! ;)

Ora spiego brevemente cosa fanno questi 3 Timer:

Il primo agisce ogni secondo e legge i messaggi dal file di testo e li stampa nel JEditorPane principale. Serve quindi a leggere i messaggi.

Il secondo agisce ogni 3 secondi e legge i nomi degli utenti on-line da un file di testo e quindi aggiorna la lista degli utenti on-line

Il terzo infine, agisce ogni 5 secondi e legge da un file di testo i nomi degli utenti bannati e provvede quindi all'espulsione in caso che il nick in uso coincida con un nick trovato nel file di testo.

Un'altra cosa strana è che ad un mio amico la la finestra di chat si è aperta dopo quasi 1 minuto o più... è possibile che possa succedere se si ha una vecchia versione di JRE installata?

UnknownSoldier
28-08-2008, 13:15
io ho notato solo che se mandi 4/5 messaggi velocemente tipo di una sola lettera appaiono tutti insieme, ma penso sia normale..

E' normale considerando che il JEditorPane si aggiorna ogni secondo ;)

UnknownSoldier
28-08-2008, 13:17
Per quanto riguarda il charset, mi pare molto strano... prima avevo questo problema ma poi ho corretto e ora non c'è più. Possibile che possa essere un problema tuo? Anche se mi sembra difficile o_O

banryu79
28-08-2008, 13:36
Dunque, visto il modo in cui usi i file di testo, non potresti sostituirli con qualche buffer in memoria?
Scrivere e leggere dal buffer sarebbe più rapido che da file.

Per il discorso Timer/Thread: ho guardato velocemente la classe Timer: non è altro che una "facility" per i thread che devono eseguire dei task, in un certo momento e/o ogni certo intervallo di tempo, percui il problema non si pone visto che non stai assegnando task a mille mila thread ma solo a tre.

Ciao :)

@EDIT:

Per quanto riguarda il charset, mi pare molto strano... prima avevo questo problema ma poi ho corretto e ora non c'è più. Possibile che possa essere un problema tuo?

non ne ho idea :fagiano:

UnknownSoldier
28-08-2008, 13:39
Dunque, visto il modo in cui usi i file di testo, non potresti sostituirli con qualche buffer in memoria?
Scrivere e leggere dal buffer sarebbe più rapido che da file.

Per il discorso Timer/Thread: ho guardato velocemente la classe Timer: non è altro che una "facility" per i thread che devono eseguire dei task, in un certo momento e/o ogni certo intervallo di tempo, percui il problema non si pone visto che non stai assegnando task a mille mila thread ma solo a tre.

Ciao :)

@EDIT:

non ne ho idea :fagiano:

Non ho capito bene cosa intendi con quel sistema di buffer in memoria... potresti essere più preciso?

banryu79
28-08-2008, 13:45
Non ho capito bene cosa intendi con quel sistema di buffer in memoria... potresti essere più preciso?

Considetando che non ho mai programmato nulla che non fosse strettamente classificabile come "desktop application" potrei aver detto una cavolata.

I file di testo che usi adesso, di preciso a cosa servono?
Cioè quando vengono scritti e da chi, e quando vengono letti e da chi?

@EDIT:
scusa, hai la chat, aprila che la usiamo

UnknownSoldier
28-08-2008, 13:54
Considetando che non ho mai programmato nulla che non fosse strettamente classificabile come "desktop application" potrei aver detto una cavolata.

I file di testo che usi adesso, di preciso a cosa servono?
Cioè quando vengono scritti e da chi, e quando vengono letti e da chi?

Uso i file perchè:

1) Per usare i socket avrei bisogno di un server dedicato

2) Evito di usare i database in modo che questa chat sia utilizzabile ad una più vasta gamma di utenti e con più semplicità

L'applet java fa da client per ogni utente che si collega alla chat. Siccome non ho idea di come fare per scrivere file di testo da applet java su uno spazio web, mi affido ad uno script php che svolge queste funzioni in modo molto semplice.

Quando nella pagina iniziale si inserisce un nick per entrare, l'applet java legge il file di testo che contiene la lista di tutti i nick attualmente on-line, e se trova uno stesso nick già collegato, la lettura si interrompe e viene comunicato che quel nick è già in uso. Se invece non è in uso, la finestra di chat si apre e si è quindi collegati.

Il JTextField che serve per inviare i messaggi, quando si preme invio o si preme il pulsante "Invia messaggio", invia il testo attualmente presente sul JTextField e lo invia ad uno script php attraverso una richiesta GET. Lo script php riceve questi dati e apre il file messageList.txt in modalità append e aggiunge il messaggio.

Ogni secondo il timer readerTimer effettua una lettura a messageList, e quindi aggiorna il JEditorPane se ci sono nuovi messaggi ;)

PS: sono collegato :P

UnknownSoldier
28-08-2008, 14:11
Riguardo ai blocchi, a volte che si verificano e a volte no, ecco parte del codice inerente all'invio dei messaggi:

Al JTextField ho impostato la classe per gli eventi SendMessageHandler.
mainTextArea sarebbe il JEditorPane principale che visualizza i messaggi.
Forse questi blocchi si verificano quando contemporaneamente il Timer che legge i messaggi e l'actionPerformed di SendMessageHandler vengono eseguiti?


private class SendMessageHandler implements ActionListener
{
public void actionPerformed (ActionEvent event)
{
if (!insertMessageField.getText().equals (""))
{
String message = insertMessageField.getText();

sendMessage ("all", username, message);

messageList += "\n" + username + " > " + insertMessageField.getText();
mainTextArea.setText (messageList);

insertMessageField.setText ("");
}
}
}


private void sendMessage (String to, String username, String message)
{
InputStream inputStream = null;
try
{
String address = "http://" + Host + "/DFChat/server/server.php?r=1&to="
+ to.replace (" ", "_") + "&user=" + username.replace (" ", "_") + "&message=" + message.replace (" ", "_");

URL url = new URL (address);
URLConnection connection = url.openConnection();
connection.setDoOutput (true);

inputStream = connection.getInputStream();
}
catch (Exception exception)
{
exception.printStackTrace();
}
finally
{
try
{
inputStream.close();
}
catch (Exception exception)
{
exception.printStackTrace();
}
}
}

banryu79
28-08-2008, 14:53
Il JTextField che serve per inviare i messaggi, quando si preme invio o si preme il pulsante "Invia messaggio", invia il testo attualmente presente sul JTextField e lo invia ad uno script php attraverso una richiesta GET. Lo script php riceve questi dati e apre il file messageList.txt in modalità append e aggiunge il messaggio.

Questo avviene qui (evidenziato in grassetto)

private void sendMessage (String to, String username, String message)
{
InputStream inputStream = null;
try
{
String address = "http://" + Host + "/DFChat/server/server.php?r=1&to=" + to.replace (" ", "_") + "&user=" + username.replace (" ", "_") + "&message=" + message.replace (" ", "_");

URL url = new URL (address);
URLConnection connection = url.openConnection();
connection.setDoOutput (true);

inputStream = connection.getInputStream();
}
catch (Exception exception)
{
exception.printStackTrace();
}
finally
{
try
{
inputStream.close();
}
catch (Exception exception)
{
exception.printStackTrace();
}
}
}


Nota(1): il codice in blu probabilmente non serve a nulla.

Nota(2): il metodo sendMessage() viene chiamato dal metodo actionPerformed() che rappresenta la risposta all'evento "action" del JTextField: siccome il thread esecutore di questo pezzo di codice è l'AWT Event Thread, la latenza ptrebbe essere eliminata facendo eseguire sendMessage() ad un thread apposito, e liberando così l'AWT event thread, con un migliore risultato nella percezione di reattività che l'utente ha del client della tua chat.

Prova così:


private void sendMessage (String to, String username, String message)
{
try
{
Thread senderThread = new Thread()
{
@Override
public void run()
{
String address = "http://" + Host + "/DFChat/server/server.php?r=1&to=" + to.replace (" ", "_") + "&user=" + username.replace (" ", "_") + "&message=" + message.replace (" ", "_");

URL url = new URL (address);
URLConnection connection = url.openConnection();
}
};
senderThread.start();
}
catch (Exception exception)
{
exception.printStackTrace();
}
}



DISCLAIMER: sono un niubbo della programmazione, potrei quindi averti consigliato fesserie ma due cose giocano in mio favore:
1) la possibilità che altri utenti corregano eventuali errori;
2) il fatto che tentar non nuoce: fai un test.

Ciao :)

UnknownSoldier
28-08-2008, 14:56
Grazie potrei provare. Comunque l'InputStream mi serve. Se non eseguo getInputStream(), la chiamata allo script php non viene eseguita.

banryu79
28-08-2008, 14:57
Grazie potrei provare. Comunque l'InputStream mi serve. Se non eseguo getInputStream(), la chiamata allo script php non viene eseguita.

Ah, non lo sapevo, non so niente di web e php.
Si sa il perchè?

UnknownSoldier
28-08-2008, 15:04
Ah, non lo sapevo, non so niente di web e php.
Si sa il perchè?

Non lo so, ho solo provato a toglierlo, e non va ;)

banryu79
28-08-2008, 15:24
Non lo so, ho solo provato a toglierlo, e non va ;)

Forse ho capito perchè.
Leggendo qui (http://java.sun.com/j2se/1.4.2/docs/api/java/net/URLConnection.html) si evince che:


The abstract class URLConnection is the superclass of all classes that represent a communications link between the application and a URL. Instances of this class can be used both to read from and to write to the resource referenced by the URL. In general, creating a connection to a URL is a multistep process:

1. The connection object is created by invoking the openConnection method on a URL.
2. The setup parameters and general request properties are manipulated.
3. The actual connection to the remote object is made, using the connect method.
4. The remote object becomes available. The header fields and the contents of the remote object can be accessed.


e anche che:


The following methods are used to access the header fields and the contents after the connection is made to the remote object:

* getContent
* getHeaderField
* getInputStream
* getOutputStream


Siccome nel tuo codice non chiami esplicitamente il metodo connect(), la connessione all'oggetto remoto non avviene (e quindi lo script php non viene invocato) e quindi quando il codice esegue connection.getInputStream(), che come specificato sopra necessita che la connessione sia stabilita, siccome la connessione non è ancora stata aperta la apre lui dietro le quinte (suppongo).

Quindi puoi rimuovere la chiamata e il blocco try{}catch(){}finally{} relativo all'oggetto InputStream e sostituire il tutto con una chiamata esplicita al metodo connect().

Javadoc rulez!:O

UnknownSoldier
28-08-2008, 16:07
Forse ho capito perchè.
Leggendo qui (http://java.sun.com/j2se/1.4.2/docs/api/java/net/URLConnection.html) si evince che:



e anche che:



Siccome nel tuo codice non chiami esplicitamente il metodo connect(), la connessione all'oggetto remoto non avviene (e quindi lo script php non viene invocato) e quindi quando il codice esegue connection.getInputStream(), che come specificato sopra necessita che la connessione sia stabilita, siccome la connessione non è ancora stata aperta la apre lui dietro le quinte (suppongo).

Quindi puoi rimuovere la chiamata e il blocco try{}catch(){}finally{} relativo all'oggetto InputStream e sostituire il tutto con una chiamata esplicita al metodo connect().

Javadoc rulez!:O

Ma connect() sarebbe un metodo di URLConnection?

Ritornando a prima, non capisco perchè in locale non mi da blocchi e on-line sì... comunque ora provo a fare come mi hai detto ;) Però aggiungendo anche il metodo invokeLater, che secondo me indispensabile per dare una maggiore robustezza nella gestione dei componenti Swing.

banryu79
28-08-2008, 16:25
Ma connect() sarebbe un metodo di URLConnection?

Leggi il link che ti ho postato.

Ritornando a prima, non capisco perchè in locale non mi da blocchi e on-line sì... comunque ora provo a fare come mi hai detto ;)
Forse perchè in locale la pagina php risiede nel tuo pc oppure in un pc nella tua lan e fa molto prima...


Però aggiungendo anche il metodo invokeLater, che secondo me indispensabile per dare una maggiore robustezza nella gestione dei componenti Swing.

Attenzione. Leggi qua (http://java.sun.com/javase/6/docs/api/javax/swing/SwingUtilities.html#invokeLater(java.lang.Runnable)).
Con invokeLater accodi all'AWT Event Thread un oggetto Runnable (un Thread è un oggetto Runnable) perchè venga eseguito in maniera asincrona dall'AWT Event Thread.

Quello che invece io ti ho postato, è un metodo per eseguire sendMessage() fuori dall'AWT Event Thread, in un altro Thread.
Questo proprio allo scopo di non rallentare il processing dei messaggi e update dell'interfaccia grafica.

E' chiara la differenza?

UnknownSoldier
28-08-2008, 16:26
We io sono on-line, sembra che gli scatti siano diminuiti di parecchio... se questa è la buona strada allora dovrei creare altri thread separati per le altre funzioni, così da eliminare al 100% blocchi indesiderati. Magari se puoi collegarti in chat proviamo insieme ;)

banryu79
28-08-2008, 16:44
Sì, ma è importante che tutte queste operazioni le fai eseguire ai diversi thread come ti ho mostrato e non le accodi nell'AWT Event Dispatcher.

L'AWT-Event-Dispatcher dovrebbe eseguire solo codice relativo all'aggiornamento dei Component grafici.

UnknownSoldier
28-08-2008, 16:51
Sì, ma è importante che tutte queste operazioni le fai eseguire ai diversi thread come ti ho mostrato e non le accodi nell'AWT Event Dispatcher.

L'AWT-Event-Dispatcher dovrebbe eseguire solo codice relativo all'aggiornamento dei Component grafici.

Ok grazie, ora sto cercando di creare nuovi Thread appositi per ogni operazione. Oh ma non li ricevi i messaggi che ti sto inviando in chat? :P

UnknownSoldier
28-08-2008, 16:54
Scusa ma l'AWT Event dispatcher non è lo stesso di quello che viene gestito con invokeLater di SwingUtilities giusto?

banryu79
29-08-2008, 09:26
Scusa ma l'AWT Event dispatcher non è lo stesso di quello che viene gestito con invokeLater di SwingUtilities giusto?
Sì.
E' il thread che recupera e processa gli eventi AWT nella AWT-Event-Queue.
Comunque ho provato la chat stamattina e fila liscia, bello.

Ciao :)

UnknownSoldier
29-08-2008, 10:19
Sì.
E' il thread che recupera e processa gli eventi AWT nella AWT-Event-Queue.
Comunque ho provato la chat stamattina e fila liscia, bello.

Ciao :)

Perfetto, grazie mille, mi sei stato tanto d'aiuto ^^
Però vorrei correggere una cosa... ho notato che se un messaggio è troppo lungo, una o due lettere vengono coperte dalla scroll bar verticale prima di mandare il testo a capo... come potrei correggere?

banryu79
29-08-2008, 10:38
Perfetto, grazie mille, mi sei stato tanto d'aiuto ^^

Pagare birra! :D
In realtà non ho fatto altro che consultare i Javadoc relativamente a quei metodi/classi di cui non conoscevo il funzionamento nel contesto dell'applicazione.
Ti consiglio di fare uso costante della documentazione che Sun mette a disposizione tramite i suoi siti web.

Javadoc e i Sun Tutorial sono strumentoi tanto essenziali quanto l'IDE in cui uno sviluppatore Java lavora: usali anche tu.


Però vorrei correggere una cosa... ho notato che se un messaggio è troppo lungo, una o due lettere vengono coperte dalla scroll bar verticale prima di mandare il testo a capo... come potrei correggere?
Bisogna vedere come è definito e settato il JEditorPane, e come viene aggiunto allo scollpane (immagino sia inserito in uno scrollpane).

Il mio consiglio: esamina bene la parte di codice che crea & gestisce il Component che usi per stampare il testo e poi consulta i tutorial Sun per quel Component: sono certo che sarai in grado di sistemare il problema.

Ciao e buon lavoro :)

UnknownSoldier
29-08-2008, 11:02
Pagare birra! :D
In realtà non ho fatto altro che consultare i Javadoc relativamente a quei metodi/classi di cui non conoscevo il funzionamento nel contesto dell'applicazione.
Ti consiglio di fare uso costante della documentazione che Sun mette a disposizione tramite i suoi siti web.

Javadoc e i Sun Tutorial sono strumentoi tanto essenziali quanto l'IDE in cui uno sviluppatore Java lavora: usali anche tu.


Bisogna vedere come è definito e settato il JEditorPane, e come viene aggiunto allo scollpane (immagino sia inserito in uno scrollpane).

Il mio consiglio: esamina bene la parte di codice che crea & gestisce il Component che usi per stampare il testo e poi consulta i tutorial Sun per quel Component: sono certo che sarai in grado di sistemare il problema.

Ciao e buon lavoro :)

Il problema è che non ci metto 1 secondo a capire bene le frasi in inglese :P Ok proverò a cercare meglio ;)