Java RMI ConnectIOException bij callbacks

Pagina: 1
Acties:

  • Swinnio
  • Registratie: Maart 2001
  • Laatst online: 01-12 12:05
Heb het volgende probleem bij een RMI client-server systeem, waarbij de clients zich als event listener kunnen registreren. Het versturen van events geschiedt door middel van callbacks naar de geregistreerde clients. In dat geval heeft de server de rol van RMI client. Het versturen van deze events gebeurt in een aparte thread, zodat het geen invloed heeft op inkomende remote method calls bij de server. Nu het volgende probleem:
sommige clients "verdwijnen" zonder zich als listener te hebben afgemeld. Dat kan bijvoorbeeld gebeuren door netwerkproblemen of doordat de client in hibernate gaat, crasht o.i.d. De server krijgt dit uiteraard niet mee (weak references voor het opslaan van de listeners werkt via RMI niet) en probeert de betreffende listener een event te sturen. Dit doe hij door de List met listeners te locken en in een for-loop elk het event te sturen. Nu duurt het soms tot wel 15 minuten voordat deze callback met een exception komt (een java.rmi.ConnectIOException: Exception creating connection to: xxx.xxx.xxx.xxx; nested exception is: java.net.NoRouteToHostException: No route to host).
Het locken van de listeners heeft tot gevolg dat gedurende deze 15 minuten andere clients zich niet kunnen aanmelden (als listener registeren) of afmelden (als listener deregistreren). Mijn vraag is dus: hoe kan ik sneller ontdekken dat een client niet meer reageert en dat de callback dus opgegeven moet worden?

If the world wouldn't suck, we'd all fall off


  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024

Alarmnummer

-= Tja =-

Het eerste wat je kunt doen is niet de lijst zo lang te locken. Maak gewoon een copy van die lijst op het moment dat je de lijst gaat aanpassen (techniek heet copy on write, zoek maar eens in de concurrency libraries van java 5). Alle acties die niets aan die lijst gaan aanpassen, die mogen dus werken met een immutable versie: een snapshot van een bepaald moment in de tijd. Op deze manier kun je het locken tot een minimum beperken.

Het voordeel is dat je lockcontention (het probleem waar jij dus nu extreem veel last van hebt) kunt voorkomen. En in de meeste gevallen maakt het niet uit of de data iets out of date is (je moet dan wel rekening houden in je algorithmes dat dingen niet helemaal up to date zijn, maar dat moet je sowieso in een distributed/multithreaded omgeving).

Wat je eventueel ook kunt doen (ipv callbacks) is de client de server te pollen. Hierdoor hoef je dus geen callbacks te doen en leg je de verantwoordelijkheid volledig bij de client neer. Het voordeel van pollen is dat clients een tijdje offline kunnen gaan, daarna weer online, en ze kunnen dan weer de draad oppakken.

Ik weet verder niet genoeg van je systeem af om te zeggen of dit goeie oplossingen zijn, maar dit zijn wel technieken die ik in het verleden heb toegepast en die wel voor robuste oplossingen zorgen.

Instellen van die timeout periode zou misschien de quick fix zijn, maar ik denk dat je een goed begin maakt door niet zo lang te locken *bad sign imho*

[ Voor 17% gewijzigd door Alarmnummer op 18-12-2006 17:15 ]


  • Swinnio
  • Registratie: Maart 2001
  • Laatst online: 01-12 12:05
Pollen ipv callbacks zou grote veranderingen in de code betekenen.
Maar copy on write is misschien wel een goede oplossing. Mijn listeners bevinden zich nu in een arraylist. Als ik het goed gezien heb, is er zelfs een CopyOnWriteArrayList<E> die ik daarvoor zou kunnen gebruiken. Of is dat precies wat je bedoelt in de concurrency libraries?

If the world wouldn't suck, we'd all fall off


  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024

Alarmnummer

-= Tja =-

Swinnio schreef op maandag 18 december 2006 @ 17:28:
Pollen ipv callbacks zou grote veranderingen in de code betekenen.
Maar copy on write is misschien wel een goede oplossing. Mijn listeners bevinden zich nu in een arraylist. Als ik het goed gezien heb, is er zelfs een CopyOnWriteArrayList<E> die ik daarvoor zou kunnen gebruiken. Of is dat precies wat je bedoelt in de concurrency libraries?
Dat is wat ik bedoel :) En keep ons posted van de performance veranderingen.

  • Swinnio
  • Registratie: Maart 2001
  • Laatst online: 01-12 12:05
Dat lijkt te werken. Heb hier nog wat achtergrondinformatie m.b.t. CopyOnWriteArrayList/Set gevonden. Grappig dat daar ook stond dat het vaak gebruikt wordt voor het opslaan van listeners.
Echter, mijn probleem is hiermee niet helemaal opgelost. Weliswaar kan ik nu listeners toevoegen of verwijderen terwijl de eventthread events aan het versturen is, maar deze thread blijft nog steeds "hangen" bij callbacks naar clients die niet meer bereikbaar zijn. Dat betekent dus dat in zo'n geval de andere clients ook geen events meer krijgen totdat de ConnectIOException voor de betreffende client is opgetreden.
Een oplossing zou misschien kunnen zijn om een thread per client te gebruiken voor het versturen van events. Maar dan moet ik eerst een manier vinden om überhaupt uit te vinden welke listeners met welke clients overeenkomen (een client kan bijvoorbeeld meerdere listeners hebben geregistreerd).

If the world wouldn't suck, we'd all fall off


  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024

Alarmnummer

-= Tja =-

Swinnio schreef op dinsdag 19 december 2006 @ 09:33:
Dat lijkt te werken. Heb hier nog wat achtergrondinformatie m.b.t. CopyOnWriteArrayList/Set gevonden. Grappig dat daar ook stond dat het vaak gebruikt wordt voor het opslaan van listeners.
Echter, mijn probleem is hiermee niet helemaal opgelost. Weliswaar kan ik nu listeners toevoegen of verwijderen terwijl de eventthread events aan het versturen is, maar deze thread blijft nog steeds "hangen" bij callbacks naar clients die niet meer bereikbaar zijn. Dat betekent dus dat in zo'n geval de andere clients ook geen events meer krijgen totdat de ConnectIOException voor de betreffende client is opgetreden.
Een oplossing zou misschien kunnen zijn om een thread per client te gebruiken voor het versturen van events. Maar dan moet ik eerst een manier vinden om überhaupt uit te vinden welke listeners met welke clients overeenkomen (een client kan bijvoorbeeld meerdere listeners hebben geregistreerd).
Wat je zou kunnen doen is een pool van threads gebruiken om events naar clients te versturen. Het probleem is dan iets minder, maar uiteindelijk kan de pool wel opdrogen als er te veel 'langdurende' calls zijn. Ik zou kijken of je ergens een timeout optie kunt instellen.

misschien heb je iets aan dit topic:
http://forum.java.sun.com...=334856&messageID=1370065

Zie je de complexiteit die je krijgt als je op een synchrone manier in een distributed omgeving wilt werken? Asynchrone communicatie (kan je dus realiseren door bv te pollen; het zijn messages die je uitwisselt) leidt tot veel schonere oplossingen.

[ Voor 10% gewijzigd door Alarmnummer op 19-12-2006 10:00 ]


  • Swinnio
  • Registratie: Maart 2001
  • Laatst online: 01-12 12:05
Alarmnummer schreef op dinsdag 19 december 2006 @ 09:52:
[...]

Wat je zou kunnen doen is een pool van threads gebruiken om events naar clients te versturen. Het probleem is dan iets minder, maar uiteindelijk kan de pool wel opdrogen als er te veel 'langdurende' calls zijn. Ik zou kijken of je ergens een timeout optie kunt instellen.

misschien heb je iets aan dit topic:
http://forum.java.sun.com...=334856&messageID=1370065
Ja, daar heb ik al mee geexperimenteerd. Leek op het eerste gezicht weinig invloed te hebben. De standaard connectTimeout zou 15s moeten zijn, maar mijn clients blocken vaak veel langer. Het verlagen van deze timeout lijkt daar geen merkbaar en reproduceerbaar invloed op te hebben.
Zie je de complexiteit die je krijgt als je op een synchrone manier in een distributed omgeving wilt werken? Asynchrone communicatie (kan je dus realiseren door bv te pollen; het zijn messages die je uitwisselt) leidt tot veel schonere oplossingen.
Ik zal niet bestrijden dat asynchrone communicatie in dit geval wellicht beter was geweest, maar het is niet iets wat je zo eventjes aanpast in een bestaande applicatie :)

If the world wouldn't suck, we'd all fall off


  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024

Alarmnummer

-= Tja =-

Maar heb je al een pool van threads gebruikt? (zie de ThreadPoolExecutor voor bestaande implementatie). Met een pool van threads kun je in ieder geval een aantal callbacks parallel uitvoeren en als een er van blokkeerd voor een lange tijd, dan kunnen de andere threads nog iets doen (ipv het hele systeem blokkeerd).

[ Voor 60% gewijzigd door Alarmnummer op 19-12-2006 10:55 ]


  • Swinnio
  • Registratie: Maart 2001
  • Laatst online: 01-12 12:05
Alarmnummer schreef op dinsdag 19 december 2006 @ 10:48:
Maar heb je al een pool van threads gebruikt? (zie de ThreadPoolExecutor voor bestaande implementatie). Met een pool van threads kun je in ieder geval een aantal callbacks parallel uitvoeren en als een er van blokkeerd voor een lange tijd, dan kunnen de andere threads nog iets doen (ipv het hele systeem blokkeerd).
Ben vooralsnog niet van plan een threadpool te gebruiken. Het aantal clients is beperkt en het aantal aan- en afmeldingen als listener relatief laag. In dat geval is de penalty door steeds nieuwe threads te maken niet al te hoog. Maak nu dus voor iedere listener een eigen thread aan, die enkel die ene client events verstuurt.
Wellicht dat ik het later nog aanpas om threads te kunnen hergebruiken maar voorlopig wil ik eerst het zenden van de events laten werken bij niet-reagerende clients :)

If the world wouldn't suck, we'd all fall off


  • Kwistnix
  • Registratie: Juni 2001
  • Laatst online: 08:22
Swinnio schreef op dinsdag 19 december 2006 @ 14:36:
[...]

Ben vooralsnog niet van plan een threadpool te gebruiken. Het aantal clients is beperkt en het aantal aan- en afmeldingen als listener relatief laag. In dat geval is de penalty door steeds nieuwe threads te maken niet al te hoog. Maak nu dus voor iedere listener een eigen thread aan, die enkel die ene client events verstuurt.
Wellicht dat ik het later nog aanpas om threads te kunnen hergebruiken maar voorlopig wil ik eerst het zenden van de events laten werken bij niet-reagerende clients :)
Het idee van een thread pool is toch juist dat je geen nieuwe threads meer hoeft aan te maken, maar juist gebruik maakt van een op voorhand gedefiniëerd en gecreëerd aantal threads en dat je die hergebruikt? Volgens mij is het juist nadeliger voor de performance om iedere keer een nieuwe thread te creëren op het moment dat een listener zich registreert? Ben er zelf nog niet zo heel erg in thuis hoor, maar dat was de indruk die ik had m.b.t. thread pools.

[ Voor 6% gewijzigd door Kwistnix op 19-12-2006 15:15 ]


  • Swinnio
  • Registratie: Maart 2001
  • Laatst online: 01-12 12:05
FallenAngel666 schreef op dinsdag 19 december 2006 @ 15:14:
[...]


Het idee van een thread pool is toch juist dat je geen nieuwe threads meer hoeft aan te maken, maar juist gebruik maakt van een op voorhand gedefiniëerd en gecreëerd aantal threads en dat je die hergebruikt? Volgens mij is het juist nadeliger voor de performance om iedere keer een nieuwe thread te creëren op het moment dat een listener zich registreert? Ben er zelf nog niet zo heel erg in thuis hoor, maar dat was de indruk die ik had m.b.t. thread pools.
Ja, dat klopt helemaal. Maar thread pools zijn iets lastiger toe te voegen dan domweg nieuwe threads voor nieuwe listeners aan te maken. En omdat ik
1. eerst zeker wilde zijn dat het principe van separate threads voor elke client werkt met bestaande client code en
2. relatief weinig listeners aanmaak maar niet weet hoeveel er in totaal zullen zijn
heb ik ervoor gekozen in eerste instantie geen threadpool te gebruiken.

If the world wouldn't suck, we'd all fall off


  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024

Alarmnummer

-= Tja =-

Swinnio schreef op dinsdag 19 december 2006 @ 16:21:
[...]

Ja, dat klopt helemaal. Maar thread pools zijn iets lastiger toe te voegen dan domweg nieuwe threads voor nieuwe listeners aan te maken. En omdat ik
1. eerst zeker wilde zijn dat het principe van separate threads voor elke client werkt met bestaande client code en
ThreadPools (executors) toevoegen is echt peanuts. Het is puur een kwestie van een runnable maken met daarin de callback naar de client, en deze runnable te plaatsen in een executor. Door bv max 10 threads in een threadpool toe te laten, weet je verder dat je nooit meer dan 10 threads voor clientcallback afhandeling kunt krijgen.
2. relatief weinig listeners aanmaak maar niet weet hoeveel er in totaal zullen zijn
heb ik ervoor gekozen in eerste instantie geen threadpool te gebruiken.
Als je threads on the fly aan gaat maken, kun je 'non graceful degradation' problemen krijgen.

  • Swinnio
  • Registratie: Maart 2001
  • Laatst online: 01-12 12:05
Alarmnummer schreef op dinsdag 19 december 2006 @ 16:41:
[...]
ThreadPools (executors) toevoegen is echt peanuts. Het is puur een kwestie van een runnable maken met daarin de callback naar de client, en deze runnable te plaatsen in een executor. Door bv max 10 threads in een threadpool toe te laten, weet je verder dat je nooit meer dan 10 threads voor clientcallback afhandeling kunt krijgen.
M.b.v. java.util.concurrent.ThreadPoolExecutor? Zoals gezegd: ik wilde eerst het principe aan de gang hebben en kan nu altijd nog besluiten de threads in een pool te gooien en te hergebruiken.
Als je threads on the fly aan gaat maken, kun je 'non graceful degradation' problemen krijgen.
Je bedoelt dat het aantal Threads kan blijven groeien naarmate de applicatie langer loopt en er meer clients zijn die "verdwijnen"?

If the world wouldn't suck, we'd all fall off


  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024

Alarmnummer

-= Tja =-

Swinnio schreef op dinsdag 19 december 2006 @ 17:22:
[...]
Je bedoelt dat het aantal Threads kan blijven groeien naarmate de applicatie langer loopt en er meer clients zijn die "verdwijnen"?
Dat is idd een van de problemen, en als je veel clients krijgt, kon je systeem ook wel eens in de problemen komen. Daarm moet je altijd controle houden over het aantal threads dat er actief is.
Pagina: 1