JSF webapplicatie performance
Kees Broenink (JSF expert), 27 januari 2009
JSF webapplicatie performance...................................................... 1 Applicatieserver in productie ............................................................ 2 HotSpot...................................................................................... 2 Geheugen ................................................................................... 2 Webapplicatie performance analyse .................................................. 2 JSF fases .................................................................................... 3 JSF Backing Beans ....................................................................... 3 EJB services ................................................................................ 3 Database .................................................................................... 3 Achtergrondinformatie.................................................................. 3 Algemene opmerkingen ................................................................... 5 Vervolgstappen .............................................................................. 5
Applicatieserver in productie HotSpot Java Standaard Editie 5.0 heeft twee implementaties van de HotSpot Java Virtual Machine (JVM) technologie: -client: geoptimaliseerd voor snelle start-up en beperkt geheugengebruik, geschikt voor ontwikkeling -server: geoptimaliseerd voor maximale performance; geschikt voor productie HotSpot technologie in server-mode kan intensief gebruikte Java methodes naar machine-taal compileren (JIT, just in time) en opslaan in een cache. Dit is zeer geschikt voor server-applicaties die door meerdere gebruikers aangeroepen worden. Het maakt de performance van Java in principe net zo snel als een programma dat direct is gecompileerd naar machinetaal (C, assembler en dergelijke). Als je Glassfish gebruikt kun je deze instellingen vastleggen met de Admin Console (http://host:4848) in sectie Application Server > JVM Settings > JVM Options.
Geheugen Een applicatieserver moet vele gebruikers tegelijkertijd kunnen bedienen en kan daarom behoorlijk belast gaan worden. Het is daarom aan te bevelen om een aparte machine in te richten voor de applicatieserver. Het beschikbare geheugen van de machine moet dan zoveel mogelijk toegekend worden aan de applicatieserver. Een Windows machine kan over het algemeen maximaal 2G aan een proces toekennen. Als de machine precies 2G aan fysiek geheugen heeft dan kunnen we ongeveer 3/4 deel daarvan aan de applicatieserver geven: -Xmx1536m Als de machine meer aan boord heeft is het wellicht mogelijk om de hele 2G toe te kennen aan het Java proces dat de applicatieserver draait. -Xmx2048m Standaard begint een Java proces met minder geheugen en groeit naar gelang de vraag. Dat is niet handig. Beter is om bij het opstarten direct al het maximale geheugen toe te kennen. Maakt daarom het minimum gelijk aan het maximum: -Xms1536m of -Xms2048m Als je Glassfish gebruikt kun je deze instellingen vastleggen met de Admin Console (http://host:4848) in sectie Application Server > JVM Settings > JVM Options.
Webapplicatie performance analyse
JSF fases In JSF doorloopt een POST request 6 fases. We willen achterhalen in welke fase we veel tijd kwijt zijn. We schrijven hiervoor een speciale Listener en die configureren we in de faces-config.xml. De Listener schrijft naar 'standard out' hoeveel milli-seconden elke fase in beslag nam. Fase 1 heeft een zekere overhead die niet te vermijden is, maar meer dan 200ms is verdacht. Er moet dan een expert naar kijken. Fase 2, 3 en 4 zijn over het algemeen heel snel. Als dat niet zo is dan moet er een expert naar kijken. Als de meeste tijd in fase 5 besteed wordt dan kunnen we de zelfgeschreven Java code verder analyseren door de 'action' methodes te meten. Zie hieronder. Fase 6 heeft twee taken: de nieuwe pagina samenstellen en de componenten 'state' wegschrijven. Het samenstellen van de pagina betekent het aanroepen van de getters in de zelfgeschreven Backing Beans. Als uitgesloten is (zie hieronder) dat de tijd daar besteed wordt, dan is er een probleem met het bewaren van de componenten 'state' en moet er een expert naar kijken.
JSF Backing Beans We kunnen een goed beeld krijgen waar in JSF de tijd besteed wordt door log4j debug statements te zetten aan het begin en einde van de 'zware' methodes in JSF. Denk hierbij aan de methodes die data ophalen (de getters in de pagina) en de 'action' methodes die uitgevoerd worden als er op een knop wordt geklikt.
EJB services We kunnen een goed beeld krijgen waar de tijd besteed wordt door log4j debug statements te zetten aan het begin en einde van de service methodes.
Database We kunnen een goed beeld krijgen waar de tijd besteed wordt door log4j debug statements te zetten aan het begin en einde van de JPA methodes.
Achtergrondinformatie De Listener public class LoggingPhaseListener implements PhaseListener{ private static final long serialVersionUID = -65262288427112030L; private final Logger logger = Logger.getLogger(this.getClass()); private HashMap
stamps = new HashMap(6); /** * @param evt de JSF fase indicator */ public void beforePhase(PhaseEvent event) { logger.debug("Start fase "+event.getPhaseId()); stamps.put(event.getPhaseId().getOrdinal(),System.currentTimeMillis()); }
/** * @param evt de JSF fase indicator */ public void afterPhase(PhaseEvent event) { long start = stamps.get(event.getPhaseId().getOrdinal()); long duur = System.currentTimeMillis() - start; logger.debug("Einde fase "+event.getPhaseId()+", duur: "+duur+"ms"); } /** * @return PhaseId.ANY_PHASE */ public PhaseId getPhaseId() { return PhaseId.ANY_PHASE; } }
Configuratie Listener xxx.LoggingPhaseListener
Achtergrondinformatie JSF JSF fase 1: RESTORE VIEW Als het vorige request dezelfde URL had (POST naar dezelfde pagina) haal dan de bewaarde componenten 'state' uit de 'serialized state string' en re-creƫer de componenten met deze state. JSF fase 2: APPLY REQUEST VALUES Zet de request parameter values in de bijbehorende componenten. JSF fase 3: PROCESS VALIDATIONS Converteer en valideer de request parameter values. JSF fase 4: UPDATE MODEL VALUES Copieer de geconverteerde waardes naar de Backing Beans (setters). JSF fase 5: INVOKE APPLICATION Roep de 'action' methodes aan. JSF fase 6: RENDER RESPONSE Maak de HTML aan voor de nieuwe pagina en 'serialize' de componenten 'state'.
Algemene opmerkingen De Sun/Oracle technologie-stack: - JSF voor de presentatielaag - Session EJB voor de serviceslaag inclusief transactie-management - JPA met domeinobjecten voor de persistency-laag JSF implementatie: Sun Reference Implementatie 1.2 HTML templating: facelets JPA implementatie: EclipseLink Ik zet hier nu tegenover wat de meest gangbare keuzes in de markt zijn: - JSF voor de presentatielaag - Spring voor de serviceslaag inclusief transactie-management - JPA met domeinobjecten voor de persistency-laag JSF implementatie: Sun Reference Implementatie 1.2 HTML templating: facelets JPA implementatie: Hibernate We zien dat de meeste keuzes van de Sun/Oracle stack overeenkomen met de de-facto standaarden. De verschillen zijn de volgende. EJB versus Spring Het is bekend dat EJB's extra complexiteit met zich meebrengen. Kijk alleen al naar de mogelijke tuning opties in de diverse manuals. Spring daarentegen gebruikt singleton POJO's. Deze zijn simpel want gewoon Plain Old Java Objects. Ze zijn ook herbruikbaar voor alle gebruikers omdat het threadsafe singletons zijn (application-scope) in tegenstelling tot session EJB's die voor elke gebruiker apart aangemaakt worden. En Spring ondersteunt declaratieve transactie-demarcatie in combinatie met de POJO. EclipseLink versus Hibernate Hibernate heeft een enorm trackrecord. Er zijn vele boeken en blogs te vinden waarmee je Hibernate in de vingers kunt krijgen. Het is een framework dat al heel lang bestaat en waarin een hoge kwaliteit is bereikt.
Vervolgstappen Behalve het plaatsen van log4j statements in de verdachte methodes kunnen we ook gebruik maken van speciale tooling om een compleet beeld te krijgen van CPU en geheugengebruik van alle aangeroepen Java methodes. Als een methode veel geheugen of CPU gebruikt dan kunnen we kritisch naar die code kijken en zien of dat volgens verwachting is of exceptioneel. Een goede tool is JProfiler. Deze tool is niet gratis maar kan wel gratis gedownload en gebruikt worden gedurende een bepaalde proeftijd. Als er veel tijd besteed wordt in JSF fase 1, 2, 3 of 4 dan is het verstandig om een JSF expert in te schakelen. Idem dito voor fase 6 als uitgesloten is dat de eigen 'getters' verantwoordelijk zijn voor de performance problemen. Als de EJB's traag zijn moet daarvan eerst de oorzaak gevonden worden en eventueel overgestapt worden op Spring.
Als de database de bottleneck is in bepaalde scenario's dan moeten de queries geanalyseerd worden en geoptimaliseerd. Dit kan door het aanleggen van indexen, gebruik maken van views of door een readonly query-cache te gebruiken. De cache kan door de database zelf verzorgd worden of in de Java code. In het laatste geval is EHcache (Easy Hibernate cache) een gangbaar framework.