Retour sur Devoxx 2016 : WildFly Swarm

Cette année Devoxx France s’est tenu pendant 3 jours du 20 au 22 avril au Palais des Congrès de Paris. Infotel a permis à de nombreux collaborateurs de participer à cette éventement.

Frédéric JOSEPH nous fait un retour sur WildFly Swarm


WildFly Swarm – A quoi ça sert ?

WildFly, anciennement JBoss Application Server, est un serveur d’application Java EE 7 de la société Red Hat.


WildFly Swarm est un projet se basant sur WildFly 9 et offrant une approche innovante pour le packaging d’applications Java EE en incluant le code applicatif et les dépendances du serveur au sein d’un unique jar exécutable (fat-jar ou uberjar).

Serveur d’applications monolithique vs « uberjar »

Lors de l’utilisation d’un serveur d’applications monolithique, il faut préalablement installer le serveur puis déployer l’application dessus.

Ce serveur fournit généralement bien plus de fonctionnalités (EJB, JMS, JSF…​) que celles exploitées par l’application Java EE.

Dans un uberjar « WildFly Swarm », seules les composantes de WildFly AS (on parle de « fraction ») nécessaires à l’application sont embarquées dans le jar.
On obtient ainsi un serveur d’applications personnalisé plus léger et démarrant plus rapidement qu’un serveur Java EE « classique ».

De part son fonctionnement, WildFly Swarm incite les développeurs à découper une application Java EE en plusieurs modules distincts et autonomes, afin de ne pas embarquer toute la stack Java EE dans un unique fichier jar (qui serait très volumineux).


Chacun des modules peut alors être un micro-service à part entière pouvant être remplacé, mis à jour ou déployé sur de nouvelles machines de manière indépendante des autres modules.


Du fait qu’une application soit constituée d’un unique jar exécutable, il devient également très facile d’automatiser son déploiement en l’intégrant par exemple au sein d’un conteneur docker (plugin Maven docker-maven-plugin).

Mise en oeuvre

L’utilisation de WildFly Swarm est vraiment très simple avec Maven.

Pré-requis

Les versions minimales pour exploiter WildFly Swarm sont :

  • JDK 8

  • Maven 3.2.5

Modification du POM

  • Package
    <packaging>jar</packaging>

    Il devient inutile de réaliser le package de l’application sous la forme d’un war.

    Toutefois, cela reste possible et l’on obtiendra une archive war valide qui sera encapsulée dans le uberjar à la place du jar.


  • Plugin Maven

    Ajout du plugin Maven

    <plugin>
        <groupId>org.wildfly.swarm</groupId>
        <artifactId>wildfly-swarm-plugin</artifactId>
        <version>${version.wildfly-swarm}</version>
        <configuration>
          <mainClass>com.example.rest.Main</mainClass>
          <properties>
            <swarm.http.port>8080</swarm.http.port> <!-- Already the default value -->
            <swarm.context.path>/</swarm.context.path> <!-- Already the default value -->
          </properties>
          <jvmArguments>
            <jvmArgument>-Xmx128m</jvmArgument>
          </jvmArguments>
        </configuration>
        <executions>
            <execution>
                <phase>package</phase>
                <goals>
                    <goal>create</goal>
                </goals>
            </execution>
        </executions>
    </plugin>
  • Dépendances

    Ajout des fractions du serveur d’applications dont l’application à besoin.

    Exemple pour JAX-RS

    <!-- Java EE 7 dependency -->
    <dependency>
      <groupId>javax</groupId>
      <artifactId>javaee-api</artifactId>
      <version>7.0</version>
      <scope>provided</scope>
    </dependency>
    <!-- Wildfly Swarm Fractions -->
    <dependency>
      <groupId>org.wildfly.swarm</groupId>
      <artifactId>jaxrs</artifactId>
    </dependency>

Code de l’application

@Path("/hello")
public class HelloWorldEndpoint {
 
  @GET
  @Produces("text/plain")
  public Response doGet() {
    return Response.ok("Hello from WildFly Swarm!").build();
  }
}


@ApplicationPath("/rest")
public class RestApplication extends Application {
}


public class Main {
 
    public static void main(String[] args) throws Exception {
 
        Container container = new Container();
 
        JAXRSArchive deployment = ShrinkWrap.create(JAXRSArchive.class, "my-app.war");
        deployment.addClass(RestApplication.class);
        deployment.addClass(HelloWorldEndpoint.class);
        deployment.addAllDependencies();
        container.start().deploy(deployment);
    }
}


La classe Main sert à configurer le conteneur Java EE et à déployer les ressources programmatiquement (facultatif si on package en war).

Génération/Exécution de l’application

La génération de l’archive s’effectue de manière classique.

mvn package

Le dossier target contient alors le jar de l’application ainsi qu’une archive *-swarm.jar

Si on ouvre cette archive jar, on trouve principalement :

  • un répertoire m2repo contenant l’ensemble des librairies nécessaires à l’application

  • un répertoire _bootstrap contenant le jar de l’application

  • un répertoire META-INF contenant les fichiers de configuration nécessaires au uberjar dont notamment :

    • wildfly-swarm.properties : propriétés définies par l’utilisateur (ou par défaut)

      #Generated by WildFly Swarm
      #Sun May 08 14:52:08 CEST 2016
      swarm.bundled.dependencies=true
      swarm.context.path=/
      swarm.http.port=8080
      swarm.app.artifact=hello-swarm-infotel.jar
    • MANIFEST.MF : fichier Manifest standard indiquant la classe Main de bootstrap (Main-Class), ainsi que la classe Main de l’application (Wildfly-Swarm-Main-Class).

      Manifest-Version: 1.0
      Main-Class: org.wildfly.swarm.bootstrap.Main
      WildFly-Swarm-Main-Class: com.example.rest.Main

      Les autres fichiers de configurations servent à lister les dépendances de l’application et des modules WildFly Swarm.

Le lancement de l’application s’effectue à l’aide de la commande java -jar *-warm.jar ou avec la commande maven

mvn wildfly-swarm:run

L’application est alors opérationelle comme une application déployée sur un serveur Java EE « classique ».

Et voici comment réaliser un micro-service, exposant un Web Service REST, sous la forme d’un uberjar contenant un serveur Java EE personnalisé et dont la taille est seulement de 38.6 Mo.


Pour aller plus loin

Les possibilités de WildFly Swarm vont bien au-delà des limites de cet article (security, monitoring, message JMS…​).


Quelques liens utiles :



seedstack.org : la solution open source de PSA

seedstack_image


Un besoin permanent de productivité et de cadrage

Bien avant l’apparition du langage Java, PSA construisait déjà des frameworks permettant d’accélérer le développement logiciel et de répondre au mieux à ses besoins métiers. Lorsque Java est arrivé avec son lot de frameworks et d’outils, les frameworks PSA ont évolués pour devenir des couches d’intégration autour de frameworks existants et ainsi fournir des outils communs à l’ensemble de ses développeurs. Cependant, ce travail d’intégration d’outils qui n’ont pas été prévus pour fonctionner ensemble et difficiles à configurer a fini par montrer ses limites. De nombreux projets restaient bloqués sur des frameworks obsolètes parce que « montée de version » voulait dire réécriture complète du projet. Le manque de modularité se faisait cruellement sentir. De plus, même si les outils étaient communs (Spring, Struts, etc.), ils leur manquaient de l’opinion et des bonnes pratiques. C’est à dire qu’à chaque fois qu’un développeur changeait de projet il avait tout à réapprendre: organisation, conventions, etc…


Un programme d’entreprise

Début 2013, PSA a donc décidé de rassembler une équipe pour faire face à ce problème. Composée de deux architectes et de deux à huit développeurs selon le plan de charge (moitié PSA, moitié collaborateurs Infotel en mission pour PSA), l’équipe s’est donc attelé à la tâche : réécrire une solution complète nommée SeedStack. Dès l’origine du projet, des orientations structurantes ont été faites.


Des choix novateurs

Premièrement, la stack n’intègrerait pas Spring, comme les précedants frameworks. Elle aurait un noyau plus léger et plus modulaire pour éliminer le couplage. Le micro framework Nuun.io a été choisi pour d’adresser ces éléments grace à son architecture kernel/plugin. Il confère une grande agilité à SeedStack qui a pu grandir et se réorganiser depuis deux ans sans rencontrer aucun obstacle. Chaque plugin expose ses API (Application Programming Interface) qui servent de contrats avec les développeurs et facilitent ainsi les montées de version. Certains exposent également des SPI (Service Provider Interface) permettant aux développeurs d’étendre les fonctionnalités du framework et donc de répondre à tous leurs besoins.


Mettre le métier au cœur du développement et le cadrer

Deuxièmement, il a été décidé de promouvoir des styles d’architectures de type REST (Representational State Transfer) et DDD (Domain Driven Design). Le REST via ses API hypermedia permet aux applications d’exposer des ressources Web simples à utiliser, qui garantissent une compatibilité à long terme tout en permettant une grande évolutivité. Son support a été développé en se basant sur les spécifications JAX-RS (via Jersey) et HAL. Le DDD quant à lui permet de se recentrer sur le domaine métier. Il remet la conception au coeur du travail de développeur, alors qu’elle est trop souvent délaissée au profit d’architecture de type CRUD : correspondance 1-1 entre de la base de données et l’IHM. Aucune solution satisfaisante n’étant disponible pour son implémentation, il a été décidé de développer un nouveau framework adressant la partie tactique du DDD.

Enfin, pour la partie Web, un framework a été développé pour combiner le meilleur d’AngularJs, RequireJs, et Bootstrap. L’utilisation du REST côté serveur et d’une SPA (Single Page Application) côté UI permet ainsi, le découplage du front et du back qui peuvent maintenant avoir des cycles de vie séparés. Par ailleurs, ce framework Web n’est pas en reste côté modularité car il permet via un système de fragments de composer son application à partir de modules indépendants.


Une suite Open Source

Suite à ces choix SeedStack a connu un départ rapide et les gains en productivité ne se sont pas laissés attendre. La première version a été finalisée après seulement 6 mois de développement. Dès lors, elle a été intégrée par des projets pilotes, qui ont permis à la solution de se développer et de s’affiner. Avec un rythme de 3 versions par an, SeedStack a connu des évolutions fréquentes. Début 2015, une quarantaine de projets (dont la moitié en production) utilisaient déjà cette stack. Conscient de l’intérêt de sa solution, PSA a alors décidé de publier SeedStack sous une license libre : la MPL 2.0 (Mozilla Public License 2.0). Après un audit juridique et technique ainsi qu’un peu de refactoring, la stack a été publié sur GitHub en version béta en mai 2015. Puis, après quelques mois de travail une première version open source a été publiée en Juillet. Loin d’achever cette « success story », la mise en open source lance de nouveaux défis : tout d’abord continuer d’améliorer la stack, mais aussi communiquer et trouver de nouveaux partenaires.

Des premières contributions extérieures ont été enregistrées et je vous encourage tous à venir participer. Il n’y a pas de petites contributions ! Que ce soit de la documentation, des rapports de bug ou du code, toutes les contributions sont les bienvenues.



Retour sur DevoxxFR 2015 CDI

Cette année encore, de nombreux Infoteliens ont participé au Devoxx France qui s’est tenu pendant 3 jours du 8 au 10 juin au Palais des Congrès de Paris.

François NASSOY nous fait un retour sur CDI

Introduction

Contexts and Dependancy Injection (CDI) est une spécification (JSR 299) de JAVA EE qui défini une interface de programmation pour la gestion des injections des dépendances.
Le mot d’ordre de CDI pourrait être : Avec CDI, on injecte tout dans tout!
Les principaux services offerts par CDI sont :

  • Contexte : la capacité à lier le cycle de vie et les intérractions des composants stateful ou l’ensemble des contextes est extensible.
  • L’injection de dépendance: La capacité d’injecter des componants de manière typée et de choisir le spectre d’utilisation (developpement / deploiement)

CDI offre aussi, un certain nombre de services complémentaires :

  • L’intégration de l’Expression Language (EL) permettant à tous les objets contextuels d’être utilisés directement dans les pages JSF et/ou JSP.
  • La capacité de décorer les objects injectés
  • La capacité d’associer des interceptors avec des components en utilisant typesafe interceptor bindings
  • Un modèle de notification d’evénement
  • L’ajout d’un nouveau Scope au trois classiques (request, session and application) : scope conversation
  • Une SPI permettant une intégration propre de CDI aux conteneurs tiers

Premier pas avec CDI

Injection de dépendance

L’injection avec CDI est tres simple. En effet, pour injecter un bean, il suffit d’annoter une classe, un attribut de classe, ou une méthode (setter, constructeur) avec l’annotation @Inject.

Injection d’un DAO :

  • Sur l’attribut

    @inject
    private MyClassDao myClassDao
  • Sur le setter

    private MyClassDao myClassyDao
    @inject
    public void setMyClassDao(MyClassDao myClassDao){
    return myClassDao;
    }
  • Sur le constructeur

    private MyClassDao myClassyDao;
    @inject
    public MyClass(MyClassDao myClassDao){
    this.myClassDao = myClassDao;
    }

Qualifier

Il est tres frequent qu’un bean posséde plusieurs implémentations. L’injection d’un tel bean via @inject provoque une erreur d’ambiguité. En effet, qu’elle implémentation utiliser ?
Pour pallier se problème, CDI propose les Qualifier. L’idée est que chaque implémentation soit qualifié, ie. nommé, et qu’à l’injection du bean soit spécifié quelle implémentation doit être utilisée.
Pour mettre en place cela, il faut :

  • Créer autant d’annotation que de Qualifieur annonter @Qualifier
  • Annonter les implémentations avec le Qualifier @NomQualifier
  • Ajouter lors de l’injection le Qualifier : @inject @NomQualifier
  • Création des annotations
    @Qualifier
    @Retention(RUNTIME)
    @Target({FIELD, TYPE, METHOD, PARAMETER})
    public @interface MyFirstImplDao {
    }
     
    @Qualifier
    @Retention(RUNTIME)
    @Target({FIELD, TYPE, METHOD, PARAMETER})
    public @interface MySecondImplDao {
    }
  • Annotation des implémentations

    @myFirstImplDao
    public class MyFirstImplDaoImpl implements MyClassDao {...}
     
    @mySecondImplDao
    public class MySecondImplDaoImpl implements MyClassDao {...}
  • Injection

    @inject @mySecondImplDao
    private myClassDao;

CDI defini un Qualifier par defaut via l’annotation @Default, permettant ainsi de ne pas devoir préciser le qualifier lors de l’injection.

  • L’injection
    @inject
    private myClassDao;
  • Equivaut à l’injection

    @inject @Default
    private myClassDao;

Produces

Toutes applications JAVA, ou presque, utilisent des librairies externes (log4j, Hibernate, etc.). CDI permet nativement d’inject des beans de librairie externe du moment qu’elles soient elles même des librairies CDI.

La différence entre une librairie CDI et une librairie non CDI est la présence ou non d’un fichier, beans.xml, dans l’archive. La librairie CDI devant posséder ce fichier.

CDI propose un mecanisme pour résoudre le problème d’injection de bean d’une librairie non CDI. Ce mécanisme consiste à rendre injectable un bean d’une librairie tiers en utilisant les Producer CDI, annotation @Produces. Ces produers ont pour but de de créer des objects injectables qui peuvent être de types primitifs, des beans ou encore des ressources telles qu’un entityManager, une connection JMS, etc.

Pour rendre un élement (méthode, attribut, beans, ..) injectable, il suffit de le nommer via un Qualifier et de l’annonter via @Produces.

  • Injection d’un type primitif qui ne varie pas au Runtime
    public ClassA {
    private int myNumber = 10;
     
    @Produces @MyNumber
    int getMyNumber() {
    Return myNumber;
    }
    }
    Public ClassB {
    @Inject @MyNumber
    Int myNumber;
    }

    le ClassB.myNumber est directement initialisée via ClassA.getMyNumber() à 10

  • Injection d’un type primitif qui varie au Runtime
    public ClassA {
    private java.util.Random random = new java.util.Random( System.currentTimeMillis() );
     
    java.util.Random getRandom() {
            return random;
    }
     
    @Produces @Random
    int next() {
        return getRandom().nextInt(maxNumber);
    }
    }
    public ClassB {
    @Inject @Random
    Instance<Integer> randomInt;
     
    Int myRandom;
     
    void ClassB() {
    myRandom = randomInt.get()
    }
    }

l’injection se fait sur une déclaration d’une instance contextuelle de l’objet, utilisable ensuite via la méthode get().

  • Sans CDI
    public class MyDAO {
    @PersistanceContext(unitName="cdiEM")
    private EntityManager em;
    }
  • Avec CDI
    public class DatabaseProducer() {
    @Produces
    @PersistanceContext(unitName="cdiEM")
    @MyDatabase
    private EntityManager em;
    }
    public class MyDAO {
    @inject
    @MyDatabase
    private EntityManager em;
    }

Injection d’élement d’une librairie externe comme par exemple un Logger :

  • Sans CDI
    public class MyClass {
    private static final Logger LOG = Logger.getLogger(MyClass.class);
    }
  • Avec CDI
    public class LoggerProducer() {
    @Produces
    public Logger produceLogger(InjectionPoint injectionPoint) {
    return Logger.getLogger(injectionPoint.getMember().getDeclaringClass().getName());
    }
    public class MyClass {
    @Inject
    private static final Logger LOG;
    }

    La méthode du producer à comme paramétre un injectionPoint, permettant entre autre, de récupérer les informatiosn sur le nom de la classe où l’élement est injecté.

Nommage EL

CDI permet de donner un nom EL (Expression Language) aux beans, via l’annotation @Named, afin qu’ils soient accessibles dans les pages JSF.

  • @Named
    public class MyBean {...}
  • Équivaut �
    @Named(myBean)
    public class MyBean {...}
  • Et s’utilise ainsi dans les pages JSF
    <h:outputLabel value="#{myBean.oneMethod}" />

Alternative

CDI propose une autre fonctionnalité tres interressante, la possibilité de définir une implémentation alternative à un bean.

Un bean peut avoir plusieurs implémentations utilisées à des fins différentes et positionnées lors des developpements par injection via leurs qualifiers. CDI propose de définir des implémentations qui pourront être choisies directement au moment du deploiement permettant ainsi de changer une implémentation sans modification de code.

  • Gérer une logique métier spécifique au client determinée au runtime
  • Spécifier des beans qui sont valides suivant des scénarios de deploiement spécifiques
  • Créer des versions MOCK des beans

Pour créer une alternative, il faut :

  • créer une implémentation
  • annoter la classe avec @alternative
  • definir les qualifiers impactés par l’alternative
  • déclarer l’alternative dans le beans.xml
  • Dao bean avec trois implémentations
    @myFirstImplDao
    public class MyFirstImplDaoImpl implements MyClassDao {...}
    @mySecondImplDao
    public class MySecondImplDaoImpl implements MyClassDao {...}
    @mytThirdImplDao
    public class MythirdImplDaoImpl implements MyClassDao {...}
  • Création de l’alternative
    @alternative
    public mockDAO impléments MyClassDao {}
    Ajout des qualifier devant être remplacer par l’alternative (1 ou plusieurs)
    @alternative
    @myFirstImplDao
    @mySecondImplDao
    public MockMyClassDAO impléments MyClassDao {}
  • L’injection des implémentations ne change pas
     
    @inject @mySecondImplDao
    private myClassDao;

    à ce stade, c’est toujours l’implémentation MySecondImplDao qui sera utilisée

  • Activation de l’alternative dans le beans.xml
    <alternatives>
       <class>fr.exemple.alternative.MockMyClassDAO</class>
    </alternatives>

    à partir de cette instant, c’est l’implémentation alternative, ici le mock, qui sera utilisé en lieu et place des implémentations MyFirstImplDao et MySecondImplDao.
    L’alternative ne portant pas le qualifier de l’implémentation MyThirdImplDao, les injections liés à cette implémentation ne seront pas impactées par l’alternative. Ce sera toujours l’implémentation MyThirImplDao qui sera utilisée.

Event

CDI offre aussi la possibilité aux beans de produire et de consommer des evénements (events). Ce mécansime permet une interaction entre beans via un couplage asynchrone sans utiliser JSM. Il permet aussi de synchroniser et capter les changements d’état de bean stateful.

Un évement est matérialisé soit par un event objet soit par un playload et est qualifié via un ou plusiuers event qualifiers. Ces qualifiers permettent de determiner les events à produire ou à observer pour un bean donné.

    Le principe est simple :

  • mise en place d’un event à observer
    • création d’un qualifier pour l’event
    • création de l’event
    • création de la(des) méthode(s) déclanchant l’event
  • mise en place des observer
    • création de la(des) méthode(s) écoutant l’event
  • Qualification de l’event
    @Qualifier
    @Target({FIELD, PARAMETER})
    @Retention(RUNTIME)
    public @interface Updated {}
  • Création de l’event
    @Inject @Updated
    Event<User> userEvent;
  • Création de la méthode déclanchant l’event
    public void myMethod(Event<User> userEvent) {
       ...
       userEvent.fire(user);}
  • Création d’un observateur quelque soit le qualifier de l’event
    public void onAnyUserEvent(@Observes User user) {}

    méthode déclanchée pour tout event de type userEvent

  • Création d’un observateur pour un events spécifique
    public void afterUserUpdate(@Observes @Updated User user) {...}

    méthode déclanchée pour les event qualifiés @Updated

Conclusion

Bien que cet article ne presente qu’un bref appercu des possibilités de l’API CDI, il transparait assez clairement que CDI apporte un vrai plus à JAVA EE6, de part sa facilité d’utilisation et ses nombreuses fonctionnalités. CDI est une remise en cause de la façon de traiter l’injection de dépendances et les AOP. Il simplifie, il réduit, il se débarrasse de l’héritage, des idées dépassées.

Dans le domain des injections de dépences, des AOP, etc., une API revient reguliérement, Spring. Bien que à mon sens Spring ait encore une longueur d’avance sur CDI, CDI ne démerite pas, loin de là même. Il offre une tres bonne altérnative voir un complément. En effet, des mécanismes permettent d’intégrer les deux API.



Université de la Performance -retour sur DevoxxFR 2014

Sebastien VAIRA, Senior Developper JEE / Grails revient sur une session Performance de DevoxxFR 2014

La performance, partie intégrante du travail de chacun d’entre nous, se doit d’être au cœur de nos développements ; nous ne devons pas négliger cet aspect et l’intégrer au plus vite dans nos cycles de développements. Car s’attaquer aux mesures (métriques) suite aux développements est rarement gage de réussite ; cela peut même induire une forte refonte du code, voire une remise en cause de l’architecture. Et c’est là que les plus grosses dérives se produisent…
Dans cet article, nous allons revenir sur les outils de référence et les pratiques qui ont été abordés lors du Devoxx 2014.

1. Préparer les jeux de données – Benerator


Il n’est pas toujours aisé de pouvoir disposer d’un jeu de données d’une volumétrie comparable à celle de PROD mais aussi d’une qualité semblable.
Certains d’entre nous, en particulier dans le domaine bancaire, ont également des contraintes d’anonymisation à respecter ; ce qui complexifie encore davantage la mise en place (l’acquisition) d’un jeu de données cohérent (vis-à-vis de la PROD).

Pour répondre à cette problématique, il existe l’outil : Benerator
http://databene.org/databene-benerator

Qui présente (entre autres) ces avantages :

  • Génère des données de TEST sans limite de taille
  • Rend anonymes les données sensibles
  • Insère des données en base si besoin / les ressort en fichiers sinon (CSV/XML/plat etc.)
  • Il embarque de base différents domaines (adresse, personne, web, finance etc.) pour faciliter la création de données
  • Il est multi-plateformes
  • Il supporte la majorité des BDD (Oracle, DB2, MySQL, PostgreSQL etc.)
  • Enfin, il est gratuit.

2. Exécuter une mesure unitaire – Chrome developer tools

La mesure unitaire réfère ici à un test de performance avec un utilisateur sur une base de données chargée.
Ceci peut facilement être réalisé avec Chrome DevTools qui nous fournit de nombreux composants pour :

  • tracer le temps réseau :
  • tracer la chronologie des événements (timeline) et ce qui prend du temps (chargement des ressources, parsing JS, calcul des styles etc.) :

  • tracer le temps d’exécution des scripts et l’utilisation mémoire :

3. Exécuter des tests de charge – Gatling

Cette fois par test de charge, on entend ici une cible de 100 utilisateurs par ex. et ce sur un volume BDD de PROD.
Simuler l’utilisation simultanée de votre serveur par X utilisateurs est généralement réalisé par l’écriture d’un script.
On connaissait jMeter, outil open-source d’Apache, qui réalise correctement ce travail mais qui est considéré, du moins au Devoxx, comme « ancestral ».
LoadRunner, logiciel HP payant ;
la tendance serait plutôt désormais à l’utilisation de l’outil Gatling :
http://gatling-tool.org/

Ce dernier offrant comme particularités :

  • Un vrai langage de développement pour les scripts : le Scala (il s’agit donc bien ici d’un développement à part entière dans le cadre d’un projet)
  • Un module ‘Recorder’ (sorte de proxy entre votre navigateur et votre application) vous aidant à écrire des scénarios ; pour ce faire, il suffit simplement de l’activer et de naviguer au sein de votre application Web…
  • La mise à disposition d’un plugin Maven gatling
  • La génération de rapports HTML avec graphiques très sympas (visuel + significatif) :

A noter, toujours dans la rubrique des tests de charge réalisables via Gatling, que bien souvent les tests suivants ne sont pas considérés/réalisés :
a) Test de rupture
Ayant pour objectif de déterminer les limites de votre application (en terme de nombre d’utilisateurs max ou de temps de réponse non acceptable)
b) Test de vieillissement (test d’endurance)
La capacité de votre application à fonctionner sur une période étendue (ex : 48h) avec une charge constante égale à la charge moyenne.
Il est évidemment conseillé de ne pas les négliger…

4. Industrialisation/automatisation des tests de charge – Jenkins, Capistrano, Chef

La question des performances est souvent traitée à la fin du projet ;
Ce qui implique que si elles ne sont pas acceptables, des dérives risquent d’être observées pour rectifier le tir ; l’architecture risque même d’être revue !
Ainsi donc, pour éviter ce type de faux pas, il faut intégrer les tests de performance au plus tôt dans le cycle de développement de nos projets et ce de préférence au sein d’une intégration continue.

Jenkins, outil open-source d’intégration continue, est un bon candidat pour ce faire.
http://jenkins-ci.org/

Au sein d’un job spécifique gatling, il permettrait par ex. de :

  • a) Créer un environnement
  • b) Réaliser un/des tirs de performance
  • c) Détruire ce contexte.

On pourrait ensuite visualiser les tableaux de bord Gatling résultants directement sous Jenkins.
Une des particularités intéressante en découlant serait de poser des limites sur les temps de réponse attendus pour votre application (ex : <=200 ms) ; De sorte que si un des développeurs impacte l’aspect performance, le build Jenkins soit en conséquence cassé et le développeur notifié par mail… A noter, dans l’optique de mise en place sous Jenkins d’un environnement adéquat pour réaliser les tirs de performance (ex : application déployée temporairement sur un espace virtuel), que ce dernier peut facilement être couplé aux outils suivants : ‘Chef’ et ‘Capistrano’. Chef permettant de déployer facilement serveurs et applications sur un emplacement physique/virtuel/cloud : http://www.getchef.com/chef/

Capistrano permettant d’exécuter des commandes SSH sur des serveurs distants :
http://capistranorb.com/

6. Problème de contention/identifier une fuite mémoire – VisualVM

Pour mesurer la consommation des ressources d’une application tournant sous la JVM, il est préconisé d’utiliser l’outil VisualVM, fourni dans les distributions Sun du JDK.
Egalement disponible en téléchargement ici :
http://visualvm.java.net/

Cet outil nous offre une petite interface graphique simple pour le suivi :

  • De l’utilisation CPU
  • De la consommation mémoire
  • Du volume de classes chargées par la VM
  • Des threads

Il permet aussi :

  • De réaliser le monitoring sur une application distante :
  • De réaliser un profilage CPU/mémoire :
  • Et enfin de diagnostiquer les fuites mémoires (par analyse des opérations qui consomment le plus)

Avec ce type d’outil, vous avez désormais de nombreuses possibilités pour analyser les problèmes de contention.

7. Identifier un problème d’index – jstack, explan plan

8. Mise en place du monitoring – collectd, Graphite et Metrics

  • Collectd : outil portable écrit en C et agissant tel un démon qui ‘monitore’ tout périodiquement et collecte des métriques relatives aux statistiques de performance du système
    http://collectd.org/

  • Graphite : outil écrit en Python proposant des interfaces graphiques avancées pour le suivi temps-réel des métriques de votre serveur
    http://graphite.wikidot.com/

  • Metrics : librairie Java intégrable dans votre projet via Maven et permettant de mesurer le comportement des composants critiques ; A noter qu’il se couple à Graphite pour la partie reporting.
    http://metrics.codahale.com/

  • metrics-spring : annotation Spring ‘@Timed ‘ sur les méthodes (sensibles) de votre serveur ;
    permettant d’obtenir ce type de sortie console/log:

9. Tuning système – Bonnie++

Les développeurs ne sont pas toujours à incriminer dans le cas de performances insatisfaisantes ;
N’oublions pas que les disques durs, CPU, cartes réseaux sont autant de composants qui peuvent influer sur les résultats d’une application ; et donc par conséquent l’aspect ‘Tuning d’infrastructure’ est alors à considérer.
A été abordé lors du Devoxx un petit outil de benchmark : « BONIE++ » pour réaliser une analyse système et vérifier si des optimisations sont possibles.
http://www.coker.com.au/bonnie++/

Conclusion

Cet article a plutôt vocation à être un recueil d’outils (en tout genre) pour réaliser des mesures unitaires/de charge, automatiser ces tests au sein d’une intégration continue, disposer de tableaux de bord modernes, être capable suite à l’obtention éventuelle de résultats négatifs de diagnostiquer un problème de contention/fuite mémoire/index et aller pourquoi pas jusqu’à du tuning système.
Chacun de ces outils a été abordé, ses facettes notables retranscrites ; je laisse néanmoins à chacun d’entre vous la possibilité de ‘creuser’ et de déterminer la réelle valeur ajoutée de l’outil au sein de vos projets.



Dépoussierez votre construction d’appli avec GRADLE

Les acteurs en place


GRADLE


Gradle est un moteur de production basé sur le langage Groovy fonctionnant sur une plateforme JAVA. Il permet d’automatiser les build, les tests, les publications, les déploiements, etc. pour une multitude de langages.

Apache Maven


Aujourd’hui Maven est utilisé par la majorité des développeurs pour automatiser le build et le deploy des applications.
Il se base sur un concept « Convention over configuration » qui impose le respect d’un certain nombre de règles :

  • Définition des règles de construction via des fichiers XML, les POM,
  • Nommage et structuration de répertoires projet (src/main/Java, src/main/resources, src/main/webapp, etc.).

A ce concept Maven accole un cycle de vie très stable basé sur des gaols prédéfinis (compile, test, package, install, deploy, etc.).
Les principaux reproches faits à Maven sont que

  • Maven est Monolithique, il laisse très peu de place pour les spécificités et la customisation,
  • Maven repose sur de la configuration XML qui devient rapidement peu lisible,
  • Maven propose un fichier de build par artefact,
  • Maven ne permet de Build d’un seul langage,
  • La résolution des problèmes de dépendances est difficile et fastidieuse.

Gradle avance ses atouts


Gradle reprend et agrège les points forts d’ANT, Maven, Ivy et Groove, apportant flexibilité et souplesse.
Il reprend les idées et concepts suivants :

  • Le concept « Convention over configuration » de maven
  • Le cycle de vie de Maven
  • La notion de repositorie de Maven
  • Le moteur de dépendances de Maven et Ivy
  • Le Scripting de ANT
  • Le langage de programmation de Groovy

Gradle repose sur un mécanisme de cycle de vie qui s’appuie sur un système de tâches. Ce cycle de vie est basé sur l’enchainement d’un certain nombre de tâches prédéfinies et customisable. Ce cycle de vie peut lui aussi être customisé via des taches personnalisées et dynamiques.
Définition d’une tache

task myCopy(type: Copy) {
    from 'resources'
    into 'target'
    include('**/*.txt', '**/*.xml', '**/*.properties')
 }

Cette liberté de création et modification de tâches offre à Gradle la décorrélation du build et du deploy ainsi que la gestion de plusieurs artéfacts au sien d’un même fichier build Groovy.

La définition des tâches et donc des règles de construction se fait à travers des fichiers écrit en Groovy. Un fichier de build Gradle (build.gradle) pour un projet Java tient en une ligne :

apply plugin:'java'

Gradle permet aussi une certaine souplesse dans la définition des repositories (adresse repository officiel, adresse repository local, adresse proxy, adresse serveur de partage, etc.) ainsi que dans la résolution des dépendances (remplacement de versions manuel, remplacement de dépendance manuel, etc.)
Définition de dépendance

dependencies {
    compile group: 'commons-collections', name: 'commons-collections', version: '3.2'
    testCompile group: 'junit', name: 'junit', version: '4.+'
}

Définition de repositories

repositories {
     mavenCentral()
 }

Un point fort supplémentaire de Gradle est sa notion de build incrémental qui lui permet, grâce à la définition de point d’entrées et de sorties lors de la construction des livrables, de déterminer si le build est UP-TO-DATE ou non.
La commande d’init d’un projet est :

gradle init

Enfin, pour simplifier la notion de version au sien de projet, Gradle, lors de l’initialisation du projet, génére un wrapper dédié au projet qui contient l’ensemble des informations de versions y compris la version de Gradle lui-même. Ainsi, le build du projet est portable sur n’importe quel environnement. Le wrapper contient la version de Gradle et un lien permettant de télécharger cette version. Ce lien étant paramétrable à souhait.

Par François Nassoy, Chef de Projet