SlideShare une entreprise Scribd logo
Tribulations d'un développeur Java
           dans le Cloud
              Tugdual Grall
                @tgrall




                                     1
Ce que vous allez apprendre


•   Retour d’experience d’un developpeur du “Dimanche”

•   Comprendre “mes” choix

•   Difficultés Surprises du Cloud




                                                         2
About me : “Tug”
•   CTO chez eXo depuis 2008

•   Développeur, Product Manager chez Oracle

•   Co fondateur du NantesJUG

•   @tgrall sur Twitter & RunKeeper

•   Triathlete

•   Développeur de resultri.com

                                               3
Agenda


•   Le Cloud, Pourquoi ? Comment ?

•   Les surprises Bonnes et Mauvaises

•   Et Alors ?




                                        4
Le cloud, pourquoi?
•   Besoins fonctionnels :

    •   Analyser les résultats de courses :Triathlons ou autres

    •   Partager ces résultats simplement : réseaux sociaux

•   Besoins techniques :

    •   Apprendre à développer pour le cloud

    •   Pas de gestion système, ressources disponibles

                                                                  5
6
Choisir une plateforme
•   Aout 2011, avant mon départ en vacances

•   Support du mode déconnecté

•   Java & NoSQL

•   Documentée et “Connue”

•   Gratuite

•   Disponible sur Mac OS X sans “hack”

                                              7
Choisir une plateforme
•   Aout 2011, avant mon départ en vacances

•   Support du mode déconnecté

•   Java & NoSQL

•   Documentée et “Connue”

•   Gratuite

•   Disponible sur Mac OS X sans “hack”

                                              7
Google AppEngine

•   Google PaaS

•   Languages : Java, Python and Go

•   Base de données : BigTable (NoSQL) & CloudSQL (MySQL)

•   Accès simplifié aux API et Services Google

    •   Google Cloud Storage, Analytics, Google Apps, Maps...


                                                                8
Resultri en quelques mots
•   Pour les Geeks                  •   Pour les Sportifs

    •   Pure Servlet/JSP                •   33 courses

    •   REST avec JAX-RS (Jersey)       •   46 754 résultats individuels

    •   Twitter Boostrap                •   3500 visiteurs uniques

                                        •   11 300 pages vues




                                                                           9
Traitement des données



                         10
Accès aux données?


•   Ma premiere réelle experience du monde NoSQL

    •   Mon passé (passif?) : 9 années d’Oracle, ... très SQL (JPA,Toplink, BC4J !)

    •   API : JPA, JDO, Datastore API ou autre (Objectify) ?




                                                                                      11
Accès aux données?

•   Mon choix : JDO & Datastore API

    •   Limité aux API Google (documenté dans le SDK)

    •   JDO : car la doc était la plus complète

    •   Datastore : est venu ensuite pour la simplicité



                                                          12
Resultri: Les Données




Sites “résultats”
 HTML, TXT, CSV, XML




                                               13
Resultri: Les Données




Sites “résultats”           Traitements en local
 HTML, TXT, CSV, XML         Utilisation du GAE Dev Server
                                       Export CSV




                                                             13
Resultri: Les Données




Sites “résultats”           Traitements en local              Import GAE
 HTML, TXT, CSV, XML         Utilisation du GAE Dev Server   Utilisation des outils GAE
                                       Export CSV                    Import CSV




                                                                                          13
Dev to “Cloud” 1/2
 1:   DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();



                                                                                         •
 2:   ...

                                                                                             Une fois les données
 3:   Map<String, Object> propertiesMap = result.getProperties();
 4:   properties = (String[]) propertiesMap.keySet()...;
 5:   ...
 6:
 7:
      Query q = new Query("TriathlonResult");
      q.addFilter("raceURI", Query.FilterOperator.EQUAL,race);
                                                                                             importées en local
 8:   PreparedQuery pq = datastore.prepare(q);



                                                                                         •
 9:   for (Entity result : pq.asIterable()) {
10:
11:
12:
      !    for (int i = 0; i < properties.length; i++) {

      Object o = result.getProperty(properties[i]);
                                                                                             Création d’un Fichier CSV
13:   String printValue = null;



                                                                                         •
14:   if (o != null) {
15:
16:
      !
      !
           if (o instanceof java.util.Date) {
           !    SimpleDateFormat formatter = new SimpleDateFormat("yyy ... H:mm:ss") ;
                                                                                             Utilisation des API Datastore
17:   !    !    Date date = (Date) o;



                                                                                         •
18:   !    !    printValue = formatter.format(date);
19:
20:
      !
      }
           } else if ... { ...!}
                                                                                             Trop “couteux” à faire sur le
                                                                                             Cloud
21:   out.print((printValue == null)?"":printValue);
22:   out.print(",");
23:   !    }
24:   ...




                                                                                                                             14
Dev to “Cloud” 2/2
appcfg.py upload_data
    --config_file=config.yml
    --filename=ironman-south-africa.csv
    --url=http://dev-result-db.appspot.com/remote_api
    --application=dev-result-db
    --kind=TriathlonResult

Uploading data records.
[INFO ] Logging to bulkloader-log-20120416.155812
[INFO ] Throttling transfers:
                                                                     •   Création des entités par CLI
[INFO ] Bandwidth: 250000 bytes/second
[INFO ] HTTP connections: 8/second
[INFO ] Entities inserted/fetched/modified: 20/second
[INFO ] Batch Size: 10
                                                                     •   Très rapide

                                                                     •
[INFO ] Opening database: bulkloader-progress-20120416.155812.sql3
[INFO ] Connecting to dev-result-db.appspot.com/remote_api
Please enter login credentials for dev-result-db.appspot.com
                                                                         Import des données “formatées”
Email: tugdual@gmail.com
Password for tugdual@gmail.com:
[INFO ] Starting import; maximum 10 entities per post
.................................................................
[INFO ] 1552 entities total, 0 previously transferred
[INFO ] 1552 entities (7016782 bytes) transferred in 60.4 seconds
[INFO ] All entities successfully transferre


                                                                                                          15
Recherche Full Text
•   Beta privée en Octobre...

•   Solution “Resultri”

    •   Utilisation de Cloud SQL




                                        16
Recherche Full Text
•   Beta privée en Octobre...

•   Solution “Resultri”

    •   Utilisation de Cloud SQL


            First & Last Names




BigTable                         CloudSQL
                                            16
Accès aux données
•   Attention aux quotas !




                                    17
Accès aux données
•   Utilisation de Memcache

    •   Cache disponible dans GAE

    •   Gestion du cache par le biais de l’API JCache (JSR-107) ou API Google

•   Dans Resultri:

        •   Tout Entity est automatiquement cachée




                                                                                18
Memcache




           19
Les surprises ....



                     20
Création de données
 1:   log.info(" -- Start -- ");
 2:   DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
 3:   Entity employee = new Entity("Employee");
 4:   employee.setProperty("firstName", "Nicolas");
 5:   employee.setProperty("lastName", "Martignole");
 6:   Date hireDate = new Date();
 7:   employee.setProperty("hireDate", hireDate);
 8:   employee.setProperty("attendedHrTraining", true);
 9:   datastore.put(employee);
10:   employee = new Entity("Employee");
11:   employee.setProperty("firstName", "Antonio");
12:   employee.setProperty("lastName", "Goncalvez");
13:   hireDate = new Date();
14:   employee.setProperty("hireDate", hireDate);
15:   employee.setProperty("attendedHrTraining", true);
16:   datastore.put(employee);
17:   Query q = new Query("Employee");
18:   PreparedQuery pq = datastore.prepare(q);
19:   for (Entity emp : pq.asIterable()) {
20:   !   log.info(emp.getProperty("firstName") + " "+ emp.getProperty("lastName" );
21:   }
22:   log.info(" -- End -- ");




                                                                                       21
Création de données
 1: log.info(" -- Start -- ");
 2: DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
 3: Entity employee = new Entity("Employee");
 4: employee.setProperty("firstName", "Nicolas");
 5: employee.setProperty("lastName", "Martignole");
 6: Date hireDate = new Date();
1: Apr 16, 2012 4:22:25 "hireDate", hireDate);
 7: employee.setProperty( PM com....Servlet doGet
2: INFO: -- setProperty("attendedHrTraining", true);
 8: employee. Start --
3: Apr 16, 2012 4:22:25 PM com....Servlet doGet
 9: datastore.put(employee);
4: INFO: Nicolas Martignole
10: employee = new Entity("Employee");
5: Apr 16, 2012 4:22:25 "firstName", "Antonio");
11: employee.setProperty( PM com....Servlet doGet
6: INFO: -- setProperty("lastName", "Goncalvez");
12: employee. End --
7: Apr 16, 2012 4:Date(); com.......$PersistDatastore persist
13: hireDate = new 22:34 PM
8: INFO: Time to persist"hireDate",: hireDate);
14: employee.setProperty( datastore 4 ms
15: employee.setProperty("attendedHrTraining", true);
16: datastore.put(employee);
17: Query q = new Query("Employee");
18: PreparedQuery pq = datastore.prepare(q);

20: !
               Mais où est parti Antonio????
19: for (Entity emp : pq.asIterable()) {
        log.info(emp.getProperty("firstName") + " "+ emp.getProperty("lastName" );
21: }
22: log.info(" -- End -- ");




                                                                                     21
Datastore : Queries

1:   Query q = new Query("Employee");
2:   q.addSort("lastName");
3:   q.addSort("firstName");
4:   PreparedQuery pq = datastore.prepare(q);
5:   for (Entity emp : pq.asIterable()) {
6:    o.println(emp.getProperty("lastName") +" "+ emp.getProperty("firstName") );
7:   }




                                                                                    22
Datastore : Queries

                                                                                     1:   Doe John
                                                                                     2:   Goncalvez Antonio
1:   Query q = new Query("Employee");
                                                                                     3:   Grall Briac
2:   q.addSort("lastName");                                                          4:   Grall Corentin
3:   q.addSort("firstName");                                                         5:   Grall Malo
4:   PreparedQuery pq = datastore.prepare(q);                                        6:   Grall Nolwenn
5:   for (Entity emp : pq.asIterable()) {                                            7:   Grall Tug
                                                                                     8:   Grall Virginie
6:    o.println(emp.getProperty("lastName") +" "+ emp.getProperty("firstName") );    9:   Martignole Nicolas
7:   }                                                                              10:   Tabarly Eric




                                                                                                               22
Datastore : Queries

1:   Query q = new Query("Employee");
2:   q.addFilter("lastName", FilterOperator.EQUAL , "Grall");
3:   q.addSort("lastName");
4:   q.addSort("firstName");
5:   PreparedQuery pq = datastore.prepare(q);
6:   for (Entity emp : pq.asIterable()) {
7:    o.println(emp.getProperty("lastName") + " "+ emp.getProperty("firstName") );
8:   }




                                                                                     23
Datastore : Queries

1:   Query q = new Query("Employee");
2:   q.addFilter("lastName", FilterOperator.EQUAL , "Grall");
                                                                                     1:   Grall   Briac
3:   q.addSort("lastName");                                                          2:   Grall   Corentin
4:   q.addSort("firstName");                                                         3:   Grall   Malo
5:   PreparedQuery pq = datastore.prepare(q);                                        4:   Grall   Nolwenn
6:   for (Entity emp : pq.asIterable()) {                                            5:   Grall   Tug
                                                                                     6:   Grall   Virginie
7:    o.println(emp.getProperty("lastName") + " "+ emp.getProperty("firstName") );
8:   }




                                                                                                             23
Datastore : Queries

1:   Query q = new Query("Employee");
2:   q.addFilter("lastName", FilterOperator.EQUAL , "Grall");
3:   q.addFilter("birthYear", FilterOperator.LESS_THAN_OR_EQUAL , 2000);
4:   q.addSort("lastName");
5:   q.addSort("firstName");
6:   PreparedQuery pq = datastore.prepare(q);
7:   for (Entity emp : pq.asIterable()) {
8:    o.println(emp.getProperty("lastName") + " "+ emp.getProperty("firstName") );
9:   }




                                                                                     24
Datastore : Queries

1:   Query q = new Query("Employee");
2:   q.addFilter("lastName", FilterOperator.EQUAL , "Grall");
3:   q.addFilter("birthYear", FilterOperator.LESS_THAN_OR_EQUAL , 2000);
4:   q.addSort("lastName");

                                                                                     ?
5:   q.addSort("firstName");
6:   PreparedQuery pq = datastore.prepare(q);
7:   for (Entity emp : pq.asIterable()) {
8:    o.println(emp.getProperty("lastName") + " "+ emp.getProperty("firstName") );
9:   }




                                                                                         24
Datastore : Queries

1:   Query q = new Query("Employee");
2:   q.addFilter("lastName", FilterOperator.EQUAL , "Grall");
3:   q.addFilter("birthYear", FilterOperator.LESS_THAN_OR_EQUAL , 2000);
4:   q.addSort("lastName");
     java.lang.IllegalArgumentException:

                                                                                                   ?
5:   q.addSort("firstName");
        The first sort property must be the same as the property to which the inequality filter is applied.
6:   PreparedQuery pq = datastore.prepareproperty is firstName but the inequality filter is on birthYear
        In your query the first sort (q);
7:   for (Entity emp : pq.asIterable()) {
8:    o.println(emp.getProperty("lastName") + " "+ emp.getProperty("firstName") );
9:   }




                                                                                                              24
Datastore : Queries


1:   PersistenceManager pm = PMF.get().getPersistenceManager();
2:   Query q = pm.newQuery(Employee.class,"lastName=='Grall'&& firstName=='Malo'");
3:   List<Employee> results = (List<Employee>) q.execute(); !
4:   o.println(results);




                                                                                      25
Datastore : Queries


1:   PersistenceManager pm = PMF.get().getPersistenceManager();
2:   Query q = pm.newQuery(Employee.class,"lastName=='Grall'&& firstName=='Malo'");
3:   List<Employee> results = (List<Employee>) q.execute(); !
                                                                                      1: Grall Malo
4:   o.println(results);




                                                                                                      25
Datastore : Queries


1:   PersistenceManager pm = PMF.get().getPersistenceManager();
2:   Query q = pm.newQuery(Employee.class,"lastName=='Grall'|| firstName=='Malo'");
3:   List<Employee> results = (List<Employee>) q.execute(); !
4:   o.println(results);




                                                                                      26
Datastore : Queries


1:   PersistenceManager pm = PMF.get().getPersistenceManager();
2:   Query q = pm.newQuery(Employee.class,"lastName=='Grall'|| firstName=='Malo'");
3:
4:
     List<Employee> results = (List<Employee>) q.execute(); !
     o.println(results);                                                              ?




                                                                                          26
Datastore : Queries


 1: PersistenceManager pm = PMF.get().getPersistenceManager();
org.datanucleus.store.appengine.query.DatastoreQuery$UnsupportedDatastoreFeatureException:
 2: Problem = pm.newQuery(Employee.class,"lastName=='Grall'|| firstName=='Malo'");
     Query q with query <SELECT FROM com.grallandco.model.Employee WHERE lastName=='Grall' || firstName=='Malo'>:
 3: Or filters cannot be applied<Employee>) q.execute(); ! (found both lastName and firstName).
     List<Employee> results = (List to multiple properties
 4: o.println(results);                                                                              ?




                                                                                                                    26
Manipulation des données
•   Les données ne sont pas toujours disponibles

    •   High Data Replication (HDR) : surprenant pendant les tests

•   “Limitations” des requêtes (GQL) et JDO

•   Attentions aux index : sans index pas de “requetes”

    •   Verifier la disponibilité des index

•   Utilisation de Memcache (must have! )

                                                                     27
Autres services



                  28
Task Queues
•   Service pour l’execution de taches en arrière plan

    •    Possibilité d’inclure les taches dans un ordonnanceur (cron)

•   Resultri:

    •    Synchronization (BigTable/CloudSQL) , Nettoyage des données
        1: Queue queue = QueueFactory.getQueue("SynchappAppQueue");
        2: TaskOptions taskOptions = TaskOptions.Builder.withUrl("/worker/ProcessSynchronizeDataServlet")
        3:      .param("race-uri", "ironman-florida-2005")
        4:      .param("type", "syncDB")
        5:      .method(TaskOptions.Method.POST);
        6: queue.add(taskOptions);



                                                                                                            29
Test en Local
•   Un version de Jetty avec le SDK Google AppEngine




Simulation “Authentification Google”                    Console Developpeur


                                                                             30
Disponibilité des services




                             31
Authentification Google
       •   Utilisation de Google Account

           •   Se base sur la sécurité JavaEE standard

           •   Gestion des administrateurs par le biais de la console AppEngine

	   <security-constraint>
	   	   <web-resource-collection>
	   	   	   <url-pattern>/admin/*</url-pattern>
	   	   </web-resource-collection>
	   	   <auth-constraint>
	   	   	   <role-name>admin</role-name>
	   	   </auth-constraint>
	   </security-constraint>




                                                                                  32
URL Fetch
•   Accès à des serveurs autres que AppEngine par HTTP/HTTPS

    •    Utilisation de java.net.URL ou
         com.google.appengine.api.urlfetch.URLFetchService

•   Dans Resultri : utilisation du service de GeoCodage de Google Maps



    1:   URL url = new URL("http://maps.googleapis.com/maps/api/geocode/json?sensor=false&address="+ address );




                                                                                                                  33
Accès aux API Google
•   Google offre de nombreux Services et API

    •   Un seul compte => tous les services




                                               34
Resultri & Google
•   Les services que j’utilise pour Resultri

    •   Google Apps : Gestion du nom de domaines, Mail, ...

    •   CloudSQL : Recherche Full Text (MySQL)

    •   Cloud Storage : Sauvegarde BigTable, Serveur de fichiers

    •   Google Maps

    •   Analytics, Adsense, Webmaster


                                                                  35
En production



                36
Gratuit ?

•   Oui mais....

    •   Avec des quotas

    •   Les règles peuvent changer

        •   Septembre : Google modifie ses tarifs



                                                   37
Gratuit ?

•   Oui mais....

    •   Avec des quotas

    •   Les règles peuvent changer

        •   Septembre : Google modifie ses tarifs



                                                   37
Exemple




          38
Exemple




Import CSV
             38
Gestion du cout
•   En Fevrier : Augmentation du cout

    •   Pas d’activité utilisateur, mais cout en augmentation




                                                                39
Gestion du cout
•   En Fevrier : Augmentation du cout

    •   Pas d’activité utilisateur, mais cout en augmentation




•   Une idée ?

                                                                39
Merci Google!
•   Google Bots, et autres moteurs de recherche

    •   Utilisation de Google Webmaster Tools

    •   Mise en place d’un fichier robots.txt




                                                  40
Merci Google!
•   Google Bots, et autres moteurs de recherche
                                                  User-agent: Twiceler


    •
                                                  Disallow: /
        Utilisation de Google Webmaster Tools     User-agent: psbot
                                                  Disallow: /



    •
                                                  User-agent: *
        Mise en place d’un fichier robots.txt      Disallow: /search
                                                  Disallow: /races/*/compare
                                                  Disallow: /rest
                                                  Disallow: /races/*/*/position
                                                  Disallow: /races/*/*/rank
                                                  Disallow: /races/*/*/otherRaces
                                                  Disallow: /races/*?page=-*




                                                                                    40
Merci Google!
•   Google Bots, et autres moteurs de recherche
                                                  User-agent: Twiceler


    •
                                                  Disallow: /
        Utilisation de Google Webmaster Tools     User-agent: psbot
                                                  Disallow: /



    •
                                                  User-agent: *
        Mise en place d’un fichier robots.txt      Disallow: /search
                                                  Disallow: /races/*/compare
                                                  Disallow: /rest


    •
                                                  Disallow: /races/*/*/position
        243 784 URL bloquées sur Resultri         Disallow: /races/*/*/rank
                                                  Disallow: /races/*/*/otherRaces
                                                  Disallow: /races/*?page=-*




                                                                                    40
Console




          41
Console




          41
Console




          41
Conclusion



             42
Conclusion
•   AppEngine est-il le bon choix pour Resultri?

    •   Pour l’instant trop couteux et “puissant”

    •   Techniquement passionnant

•   Feuille de route

    •   Gestion des utilisateurs : Facebook, Twitter, Google

    •   Notifications

    •   Version Mobile

                                                               43
Conclusion
•   Techniquement : j’apprends beaucoup....   •   Publication d’articles sur mon blog

    •   NoSQL, CloudSQL, Memcache....         •    Twitter Boostrap 2 and Google Maps
                                              •    Google AppEngine Full Text Search with Cloud
    •   Utilisation de Git                         SQL
                                              •    Installing Memcached on Mac OS X and using it in
    •   Twitter Bootstrap, Less                    Java
                                              •    JAX-RS: Jersey and JSON single element arrays
                                              •    Create and Deploy a JAX-RS REST service on
                                                   Google App Engine
                                              •    ....




                                                                                                      44
Questions?




             45
Tribulations d'un développeur Java
           dans le Cloud
              Tugdual Grall
                @tgrall




                                     46

Contenu connexe

PDF
Perfug Guide de survie du développeur dans une application Java qui rame
PDF
Breizhcamp : Guide de survie du développeur dans une application (Java) qui rame
PPTX
Monitoring applicatif : Pourquoi et comment ?
PPTX
OWF12/HTML 5 local storage , olivier thomas, cto at webtyss
PDF
Optimisez vos imports de données avec Migrate
PDF
Tout ce que le getting started mongo db ne vous dira pas
PDF
Lausanne JUG - Elasticsearch
PDF
Elasticsearch - Esme sudria
Perfug Guide de survie du développeur dans une application Java qui rame
Breizhcamp : Guide de survie du développeur dans une application (Java) qui rame
Monitoring applicatif : Pourquoi et comment ?
OWF12/HTML 5 local storage , olivier thomas, cto at webtyss
Optimisez vos imports de données avec Migrate
Tout ce que le getting started mongo db ne vous dira pas
Lausanne JUG - Elasticsearch
Elasticsearch - Esme sudria

Tendances (20)

PDF
Paris data geek - Elasticsearch
PDF
Des mises à jour? Emmenez votre application Stitch encore plus loin grâce aux...
PDF
PDF
L'expérience client au centre de la donnée @AirFrance
PPTX
introduction à MongoDB
KEY
Elasticsearch - Montpellier JUG
PDF
Poitou charentes JUG - Elasticsearch
PPTX
Affichage d'un document Office sous Android
PDF
Breizhcamp 2015 - Comment (ne pas réussir à) modéliser ses data dans elastics...
PDF
[Breizhcamp 2015] MongoDB et Elastic, meilleurs ennemis ?
PDF
Modélisation de données pour MongoDB
PDF
De 20 000 à 4 millions d'utilisateurs : mode d'emploi
PDF
MongoDB : la base NoSQL qui réinvente la gestion de données
PDF
Normandy JUG - Elasticsearch
PDF
Infrastructure as code drupal
PDF
Hadoop et son écosystème - v2
PDF
Construisez votre première application MongoDB
PDF
Présentation de ElasticSearch / Digital apéro du 12/11/2014
PPTX
Atelier : Développement rapide d&rsquo;une application basée surXWiki
PDF
Finist JUG - Elasticsearch
Paris data geek - Elasticsearch
Des mises à jour? Emmenez votre application Stitch encore plus loin grâce aux...
L'expérience client au centre de la donnée @AirFrance
introduction à MongoDB
Elasticsearch - Montpellier JUG
Poitou charentes JUG - Elasticsearch
Affichage d'un document Office sous Android
Breizhcamp 2015 - Comment (ne pas réussir à) modéliser ses data dans elastics...
[Breizhcamp 2015] MongoDB et Elastic, meilleurs ennemis ?
Modélisation de données pour MongoDB
De 20 000 à 4 millions d'utilisateurs : mode d'emploi
MongoDB : la base NoSQL qui réinvente la gestion de données
Normandy JUG - Elasticsearch
Infrastructure as code drupal
Hadoop et son écosystème - v2
Construisez votre première application MongoDB
Présentation de ElasticSearch / Digital apéro du 12/11/2014
Atelier : Développement rapide d&rsquo;une application basée surXWiki
Finist JUG - Elasticsearch
Publicité

Similaire à Devoxx: Tribulation d'un développeur sur le Cloud (20)

PDF
Nouveau look pour une nouvelle vie : HTML5, Spring, NoSQL et mobilité
PDF
Nouveau look pour une nouvelle vie : HTML5, Spring, NoSQL et Mobile
PDF
Stage de fin d’études – dotcloud
PDF
Stage de fin d’études – dotcloud
PDF
Construire un data lake managé - GDG Paris - Juin 2019
PPTX
Azure Camp 9 Décembre - slides session développeurs webmedia
PDF
Spark-adabra, Comment Construire un DATALAKE ! (Devoxx 2017)
PPTX
2014 03-26-appdevseries-session3-interactingwiththedatabase-fr-phpapp01
KEY
Introduction aux RIA (Rich Internet Applications)
PPTX
Lost in serverless AWS Lambda, Google Cloud Function, Azure Function quelle s...
PPTX
Cartographie - cas concrets et bonnes pratiques de développement
PPTX
HTML5 en projet
PDF
Presentation mug-data mapper
PDF
Découvrez les nouvelles fonctionnalités de Talend 6
PPTX
Gaib19 ai intudstrialisation - azure machine learning services
ODP
Introduction à CakePHP
KEY
Paris RailsCamp 2009
PPTX
Microsoft experiences azure et asp.net core
PDF
Digital GraphTour Paris - Neo4j 4.0, les nouveautés
PPTX
Web sémantique et Web de données, et si on passait à la pratique ?
Nouveau look pour une nouvelle vie : HTML5, Spring, NoSQL et mobilité
Nouveau look pour une nouvelle vie : HTML5, Spring, NoSQL et Mobile
Stage de fin d’études – dotcloud
Stage de fin d’études – dotcloud
Construire un data lake managé - GDG Paris - Juin 2019
Azure Camp 9 Décembre - slides session développeurs webmedia
Spark-adabra, Comment Construire un DATALAKE ! (Devoxx 2017)
2014 03-26-appdevseries-session3-interactingwiththedatabase-fr-phpapp01
Introduction aux RIA (Rich Internet Applications)
Lost in serverless AWS Lambda, Google Cloud Function, Azure Function quelle s...
Cartographie - cas concrets et bonnes pratiques de développement
HTML5 en projet
Presentation mug-data mapper
Découvrez les nouvelles fonctionnalités de Talend 6
Gaib19 ai intudstrialisation - azure machine learning services
Introduction à CakePHP
Paris RailsCamp 2009
Microsoft experiences azure et asp.net core
Digital GraphTour Paris - Neo4j 4.0, les nouveautés
Web sémantique et Web de données, et si on passait à la pratique ?
Publicité

Plus de Tugdual Grall (20)

PDF
Introduction to Streaming with Apache Flink
PDF
Introduction to Streaming with Apache Flink
PDF
Fast Cars, Big Data - How Streaming Can Help Formula 1
PPTX
Lambda Architecture: The Best Way to Build Scalable and Reliable Applications!
PDF
Big Data Journey
PDF
Proud to be Polyglot - Riviera Dev 2015
PDF
Introduction to NoSQL with MongoDB - SQLi Workshop
PDF
Enabling Telco to Build and Run Modern Applications
PPTX
MongoDB and Hadoop
PDF
Proud to be polyglot
PDF
Drop your table ! MongoDB Schema Design
PDF
Devoxx 2014 : Atelier MongoDB - Decouverte de MongoDB 2.6
PDF
Some cool features of MongoDB
PDF
Building Your First MongoDB Application
PDF
Opensourceday 2014-iot
PDF
Neotys conference
PDF
Softshake 2013: Introduction to NoSQL with Couchbase
PDF
Introduction to NoSQL with Couchbase
PDF
Why and How to integrate Hadoop and NoSQL?
PDF
NoSQL Matters 2013 - Introduction to Map Reduce with Couchbase 2.0
Introduction to Streaming with Apache Flink
Introduction to Streaming with Apache Flink
Fast Cars, Big Data - How Streaming Can Help Formula 1
Lambda Architecture: The Best Way to Build Scalable and Reliable Applications!
Big Data Journey
Proud to be Polyglot - Riviera Dev 2015
Introduction to NoSQL with MongoDB - SQLi Workshop
Enabling Telco to Build and Run Modern Applications
MongoDB and Hadoop
Proud to be polyglot
Drop your table ! MongoDB Schema Design
Devoxx 2014 : Atelier MongoDB - Decouverte de MongoDB 2.6
Some cool features of MongoDB
Building Your First MongoDB Application
Opensourceday 2014-iot
Neotys conference
Softshake 2013: Introduction to NoSQL with Couchbase
Introduction to NoSQL with Couchbase
Why and How to integrate Hadoop and NoSQL?
NoSQL Matters 2013 - Introduction to Map Reduce with Couchbase 2.0

Dernier (6)

PDF
FORMATION EN Programmation En Langage C.pdf
PDF
Tendances tech 2025 - SFEIR & WENVISION.pdf
PDF
L'évolution de la création de contenu (2020-2025) : L'impact de l'IA générati...
PPTX
Presentation_Securite_Reseaux_Bac+2.pptx
PDF
Modems expliqués- votre passerelle vers Internet.pdf
PDF
FORMATION COMPLETE EN EXCEL DONE BY MR. NYONGA BRICE.pdf
FORMATION EN Programmation En Langage C.pdf
Tendances tech 2025 - SFEIR & WENVISION.pdf
L'évolution de la création de contenu (2020-2025) : L'impact de l'IA générati...
Presentation_Securite_Reseaux_Bac+2.pptx
Modems expliqués- votre passerelle vers Internet.pdf
FORMATION COMPLETE EN EXCEL DONE BY MR. NYONGA BRICE.pdf

Devoxx: Tribulation d'un développeur sur le Cloud

  • 1. Tribulations d'un développeur Java dans le Cloud Tugdual Grall @tgrall 1
  • 2. Ce que vous allez apprendre • Retour d’experience d’un developpeur du “Dimanche” • Comprendre “mes” choix • Difficultés Surprises du Cloud 2
  • 3. About me : “Tug” • CTO chez eXo depuis 2008 • Développeur, Product Manager chez Oracle • Co fondateur du NantesJUG • @tgrall sur Twitter & RunKeeper • Triathlete • Développeur de resultri.com 3
  • 4. Agenda • Le Cloud, Pourquoi ? Comment ? • Les surprises Bonnes et Mauvaises • Et Alors ? 4
  • 5. Le cloud, pourquoi? • Besoins fonctionnels : • Analyser les résultats de courses :Triathlons ou autres • Partager ces résultats simplement : réseaux sociaux • Besoins techniques : • Apprendre à développer pour le cloud • Pas de gestion système, ressources disponibles 5
  • 6. 6
  • 7. Choisir une plateforme • Aout 2011, avant mon départ en vacances • Support du mode déconnecté • Java & NoSQL • Documentée et “Connue” • Gratuite • Disponible sur Mac OS X sans “hack” 7
  • 8. Choisir une plateforme • Aout 2011, avant mon départ en vacances • Support du mode déconnecté • Java & NoSQL • Documentée et “Connue” • Gratuite • Disponible sur Mac OS X sans “hack” 7
  • 9. Google AppEngine • Google PaaS • Languages : Java, Python and Go • Base de données : BigTable (NoSQL) & CloudSQL (MySQL) • Accès simplifié aux API et Services Google • Google Cloud Storage, Analytics, Google Apps, Maps... 8
  • 10. Resultri en quelques mots • Pour les Geeks • Pour les Sportifs • Pure Servlet/JSP • 33 courses • REST avec JAX-RS (Jersey) • 46 754 résultats individuels • Twitter Boostrap • 3500 visiteurs uniques • 11 300 pages vues 9
  • 12. Accès aux données? • Ma premiere réelle experience du monde NoSQL • Mon passé (passif?) : 9 années d’Oracle, ... très SQL (JPA,Toplink, BC4J !) • API : JPA, JDO, Datastore API ou autre (Objectify) ? 11
  • 13. Accès aux données? • Mon choix : JDO & Datastore API • Limité aux API Google (documenté dans le SDK) • JDO : car la doc était la plus complète • Datastore : est venu ensuite pour la simplicité 12
  • 14. Resultri: Les Données Sites “résultats” HTML, TXT, CSV, XML 13
  • 15. Resultri: Les Données Sites “résultats” Traitements en local HTML, TXT, CSV, XML Utilisation du GAE Dev Server Export CSV 13
  • 16. Resultri: Les Données Sites “résultats” Traitements en local Import GAE HTML, TXT, CSV, XML Utilisation du GAE Dev Server Utilisation des outils GAE Export CSV Import CSV 13
  • 17. Dev to “Cloud” 1/2 1: DatastoreService datastore = DatastoreServiceFactory.getDatastoreService(); • 2: ... Une fois les données 3: Map<String, Object> propertiesMap = result.getProperties(); 4: properties = (String[]) propertiesMap.keySet()...; 5: ... 6: 7: Query q = new Query("TriathlonResult"); q.addFilter("raceURI", Query.FilterOperator.EQUAL,race); importées en local 8: PreparedQuery pq = datastore.prepare(q); • 9: for (Entity result : pq.asIterable()) { 10: 11: 12: ! for (int i = 0; i < properties.length; i++) { Object o = result.getProperty(properties[i]); Création d’un Fichier CSV 13: String printValue = null; • 14: if (o != null) { 15: 16: ! ! if (o instanceof java.util.Date) { ! SimpleDateFormat formatter = new SimpleDateFormat("yyy ... H:mm:ss") ; Utilisation des API Datastore 17: ! ! Date date = (Date) o; • 18: ! ! printValue = formatter.format(date); 19: 20: ! } } else if ... { ...!} Trop “couteux” à faire sur le Cloud 21: out.print((printValue == null)?"":printValue); 22: out.print(","); 23: ! } 24: ... 14
  • 18. Dev to “Cloud” 2/2 appcfg.py upload_data --config_file=config.yml --filename=ironman-south-africa.csv --url=http://dev-result-db.appspot.com/remote_api --application=dev-result-db --kind=TriathlonResult Uploading data records. [INFO ] Logging to bulkloader-log-20120416.155812 [INFO ] Throttling transfers: • Création des entités par CLI [INFO ] Bandwidth: 250000 bytes/second [INFO ] HTTP connections: 8/second [INFO ] Entities inserted/fetched/modified: 20/second [INFO ] Batch Size: 10 • Très rapide • [INFO ] Opening database: bulkloader-progress-20120416.155812.sql3 [INFO ] Connecting to dev-result-db.appspot.com/remote_api Please enter login credentials for dev-result-db.appspot.com Import des données “formatées” Email: [email protected] Password for [email protected]: [INFO ] Starting import; maximum 10 entities per post ................................................................. [INFO ] 1552 entities total, 0 previously transferred [INFO ] 1552 entities (7016782 bytes) transferred in 60.4 seconds [INFO ] All entities successfully transferre 15
  • 19. Recherche Full Text • Beta privée en Octobre... • Solution “Resultri” • Utilisation de Cloud SQL 16
  • 20. Recherche Full Text • Beta privée en Octobre... • Solution “Resultri” • Utilisation de Cloud SQL First & Last Names BigTable CloudSQL 16
  • 21. Accès aux données • Attention aux quotas ! 17
  • 22. Accès aux données • Utilisation de Memcache • Cache disponible dans GAE • Gestion du cache par le biais de l’API JCache (JSR-107) ou API Google • Dans Resultri: • Tout Entity est automatiquement cachée 18
  • 23. Memcache 19
  • 25. Création de données 1: log.info(" -- Start -- "); 2: DatastoreService datastore = DatastoreServiceFactory.getDatastoreService(); 3: Entity employee = new Entity("Employee"); 4: employee.setProperty("firstName", "Nicolas"); 5: employee.setProperty("lastName", "Martignole"); 6: Date hireDate = new Date(); 7: employee.setProperty("hireDate", hireDate); 8: employee.setProperty("attendedHrTraining", true); 9: datastore.put(employee); 10: employee = new Entity("Employee"); 11: employee.setProperty("firstName", "Antonio"); 12: employee.setProperty("lastName", "Goncalvez"); 13: hireDate = new Date(); 14: employee.setProperty("hireDate", hireDate); 15: employee.setProperty("attendedHrTraining", true); 16: datastore.put(employee); 17: Query q = new Query("Employee"); 18: PreparedQuery pq = datastore.prepare(q); 19: for (Entity emp : pq.asIterable()) { 20: ! log.info(emp.getProperty("firstName") + " "+ emp.getProperty("lastName" ); 21: } 22: log.info(" -- End -- "); 21
  • 26. Création de données 1: log.info(" -- Start -- "); 2: DatastoreService datastore = DatastoreServiceFactory.getDatastoreService(); 3: Entity employee = new Entity("Employee"); 4: employee.setProperty("firstName", "Nicolas"); 5: employee.setProperty("lastName", "Martignole"); 6: Date hireDate = new Date(); 1: Apr 16, 2012 4:22:25 "hireDate", hireDate); 7: employee.setProperty( PM com....Servlet doGet 2: INFO: -- setProperty("attendedHrTraining", true); 8: employee. Start -- 3: Apr 16, 2012 4:22:25 PM com....Servlet doGet 9: datastore.put(employee); 4: INFO: Nicolas Martignole 10: employee = new Entity("Employee"); 5: Apr 16, 2012 4:22:25 "firstName", "Antonio"); 11: employee.setProperty( PM com....Servlet doGet 6: INFO: -- setProperty("lastName", "Goncalvez"); 12: employee. End -- 7: Apr 16, 2012 4:Date(); com.......$PersistDatastore persist 13: hireDate = new 22:34 PM 8: INFO: Time to persist"hireDate",: hireDate); 14: employee.setProperty( datastore 4 ms 15: employee.setProperty("attendedHrTraining", true); 16: datastore.put(employee); 17: Query q = new Query("Employee"); 18: PreparedQuery pq = datastore.prepare(q); 20: ! Mais où est parti Antonio???? 19: for (Entity emp : pq.asIterable()) { log.info(emp.getProperty("firstName") + " "+ emp.getProperty("lastName" ); 21: } 22: log.info(" -- End -- "); 21
  • 27. Datastore : Queries 1: Query q = new Query("Employee"); 2: q.addSort("lastName"); 3: q.addSort("firstName"); 4: PreparedQuery pq = datastore.prepare(q); 5: for (Entity emp : pq.asIterable()) { 6: o.println(emp.getProperty("lastName") +" "+ emp.getProperty("firstName") ); 7: } 22
  • 28. Datastore : Queries 1: Doe John 2: Goncalvez Antonio 1: Query q = new Query("Employee"); 3: Grall Briac 2: q.addSort("lastName"); 4: Grall Corentin 3: q.addSort("firstName"); 5: Grall Malo 4: PreparedQuery pq = datastore.prepare(q); 6: Grall Nolwenn 5: for (Entity emp : pq.asIterable()) { 7: Grall Tug 8: Grall Virginie 6: o.println(emp.getProperty("lastName") +" "+ emp.getProperty("firstName") ); 9: Martignole Nicolas 7: } 10: Tabarly Eric 22
  • 29. Datastore : Queries 1: Query q = new Query("Employee"); 2: q.addFilter("lastName", FilterOperator.EQUAL , "Grall"); 3: q.addSort("lastName"); 4: q.addSort("firstName"); 5: PreparedQuery pq = datastore.prepare(q); 6: for (Entity emp : pq.asIterable()) { 7: o.println(emp.getProperty("lastName") + " "+ emp.getProperty("firstName") ); 8: } 23
  • 30. Datastore : Queries 1: Query q = new Query("Employee"); 2: q.addFilter("lastName", FilterOperator.EQUAL , "Grall"); 1: Grall Briac 3: q.addSort("lastName"); 2: Grall Corentin 4: q.addSort("firstName"); 3: Grall Malo 5: PreparedQuery pq = datastore.prepare(q); 4: Grall Nolwenn 6: for (Entity emp : pq.asIterable()) { 5: Grall Tug 6: Grall Virginie 7: o.println(emp.getProperty("lastName") + " "+ emp.getProperty("firstName") ); 8: } 23
  • 31. Datastore : Queries 1: Query q = new Query("Employee"); 2: q.addFilter("lastName", FilterOperator.EQUAL , "Grall"); 3: q.addFilter("birthYear", FilterOperator.LESS_THAN_OR_EQUAL , 2000); 4: q.addSort("lastName"); 5: q.addSort("firstName"); 6: PreparedQuery pq = datastore.prepare(q); 7: for (Entity emp : pq.asIterable()) { 8: o.println(emp.getProperty("lastName") + " "+ emp.getProperty("firstName") ); 9: } 24
  • 32. Datastore : Queries 1: Query q = new Query("Employee"); 2: q.addFilter("lastName", FilterOperator.EQUAL , "Grall"); 3: q.addFilter("birthYear", FilterOperator.LESS_THAN_OR_EQUAL , 2000); 4: q.addSort("lastName"); ? 5: q.addSort("firstName"); 6: PreparedQuery pq = datastore.prepare(q); 7: for (Entity emp : pq.asIterable()) { 8: o.println(emp.getProperty("lastName") + " "+ emp.getProperty("firstName") ); 9: } 24
  • 33. Datastore : Queries 1: Query q = new Query("Employee"); 2: q.addFilter("lastName", FilterOperator.EQUAL , "Grall"); 3: q.addFilter("birthYear", FilterOperator.LESS_THAN_OR_EQUAL , 2000); 4: q.addSort("lastName"); java.lang.IllegalArgumentException: ? 5: q.addSort("firstName"); The first sort property must be the same as the property to which the inequality filter is applied. 6: PreparedQuery pq = datastore.prepareproperty is firstName but the inequality filter is on birthYear In your query the first sort (q); 7: for (Entity emp : pq.asIterable()) { 8: o.println(emp.getProperty("lastName") + " "+ emp.getProperty("firstName") ); 9: } 24
  • 34. Datastore : Queries 1: PersistenceManager pm = PMF.get().getPersistenceManager(); 2: Query q = pm.newQuery(Employee.class,"lastName=='Grall'&& firstName=='Malo'"); 3: List<Employee> results = (List<Employee>) q.execute(); ! 4: o.println(results); 25
  • 35. Datastore : Queries 1: PersistenceManager pm = PMF.get().getPersistenceManager(); 2: Query q = pm.newQuery(Employee.class,"lastName=='Grall'&& firstName=='Malo'"); 3: List<Employee> results = (List<Employee>) q.execute(); ! 1: Grall Malo 4: o.println(results); 25
  • 36. Datastore : Queries 1: PersistenceManager pm = PMF.get().getPersistenceManager(); 2: Query q = pm.newQuery(Employee.class,"lastName=='Grall'|| firstName=='Malo'"); 3: List<Employee> results = (List<Employee>) q.execute(); ! 4: o.println(results); 26
  • 37. Datastore : Queries 1: PersistenceManager pm = PMF.get().getPersistenceManager(); 2: Query q = pm.newQuery(Employee.class,"lastName=='Grall'|| firstName=='Malo'"); 3: 4: List<Employee> results = (List<Employee>) q.execute(); ! o.println(results); ? 26
  • 38. Datastore : Queries 1: PersistenceManager pm = PMF.get().getPersistenceManager(); org.datanucleus.store.appengine.query.DatastoreQuery$UnsupportedDatastoreFeatureException: 2: Problem = pm.newQuery(Employee.class,"lastName=='Grall'|| firstName=='Malo'"); Query q with query <SELECT FROM com.grallandco.model.Employee WHERE lastName=='Grall' || firstName=='Malo'>: 3: Or filters cannot be applied<Employee>) q.execute(); ! (found both lastName and firstName). List<Employee> results = (List to multiple properties 4: o.println(results); ? 26
  • 39. Manipulation des données • Les données ne sont pas toujours disponibles • High Data Replication (HDR) : surprenant pendant les tests • “Limitations” des requêtes (GQL) et JDO • Attentions aux index : sans index pas de “requetes” • Verifier la disponibilité des index • Utilisation de Memcache (must have! ) 27
  • 41. Task Queues • Service pour l’execution de taches en arrière plan • Possibilité d’inclure les taches dans un ordonnanceur (cron) • Resultri: • Synchronization (BigTable/CloudSQL) , Nettoyage des données 1: Queue queue = QueueFactory.getQueue("SynchappAppQueue"); 2: TaskOptions taskOptions = TaskOptions.Builder.withUrl("/worker/ProcessSynchronizeDataServlet") 3: .param("race-uri", "ironman-florida-2005") 4: .param("type", "syncDB") 5: .method(TaskOptions.Method.POST); 6: queue.add(taskOptions); 29
  • 42. Test en Local • Un version de Jetty avec le SDK Google AppEngine Simulation “Authentification Google” Console Developpeur 30
  • 44. Authentification Google • Utilisation de Google Account • Se base sur la sécurité JavaEE standard • Gestion des administrateurs par le biais de la console AppEngine <security-constraint> <web-resource-collection> <url-pattern>/admin/*</url-pattern> </web-resource-collection> <auth-constraint> <role-name>admin</role-name> </auth-constraint> </security-constraint> 32
  • 45. URL Fetch • Accès à des serveurs autres que AppEngine par HTTP/HTTPS • Utilisation de java.net.URL ou com.google.appengine.api.urlfetch.URLFetchService • Dans Resultri : utilisation du service de GeoCodage de Google Maps 1: URL url = new URL("http://maps.googleapis.com/maps/api/geocode/json?sensor=false&address="+ address ); 33
  • 46. Accès aux API Google • Google offre de nombreux Services et API • Un seul compte => tous les services 34
  • 47. Resultri & Google • Les services que j’utilise pour Resultri • Google Apps : Gestion du nom de domaines, Mail, ... • CloudSQL : Recherche Full Text (MySQL) • Cloud Storage : Sauvegarde BigTable, Serveur de fichiers • Google Maps • Analytics, Adsense, Webmaster 35
  • 49. Gratuit ? • Oui mais.... • Avec des quotas • Les règles peuvent changer • Septembre : Google modifie ses tarifs 37
  • 50. Gratuit ? • Oui mais.... • Avec des quotas • Les règles peuvent changer • Septembre : Google modifie ses tarifs 37
  • 51. Exemple 38
  • 53. Gestion du cout • En Fevrier : Augmentation du cout • Pas d’activité utilisateur, mais cout en augmentation 39
  • 54. Gestion du cout • En Fevrier : Augmentation du cout • Pas d’activité utilisateur, mais cout en augmentation • Une idée ? 39
  • 55. Merci Google! • Google Bots, et autres moteurs de recherche • Utilisation de Google Webmaster Tools • Mise en place d’un fichier robots.txt 40
  • 56. Merci Google! • Google Bots, et autres moteurs de recherche User-agent: Twiceler • Disallow: / Utilisation de Google Webmaster Tools User-agent: psbot Disallow: / • User-agent: * Mise en place d’un fichier robots.txt Disallow: /search Disallow: /races/*/compare Disallow: /rest Disallow: /races/*/*/position Disallow: /races/*/*/rank Disallow: /races/*/*/otherRaces Disallow: /races/*?page=-* 40
  • 57. Merci Google! • Google Bots, et autres moteurs de recherche User-agent: Twiceler • Disallow: / Utilisation de Google Webmaster Tools User-agent: psbot Disallow: / • User-agent: * Mise en place d’un fichier robots.txt Disallow: /search Disallow: /races/*/compare Disallow: /rest • Disallow: /races/*/*/position 243 784 URL bloquées sur Resultri Disallow: /races/*/*/rank Disallow: /races/*/*/otherRaces Disallow: /races/*?page=-* 40
  • 58. Console 41
  • 59. Console 41
  • 60. Console 41
  • 62. Conclusion • AppEngine est-il le bon choix pour Resultri? • Pour l’instant trop couteux et “puissant” • Techniquement passionnant • Feuille de route • Gestion des utilisateurs : Facebook, Twitter, Google • Notifications • Version Mobile 43
  • 63. Conclusion • Techniquement : j’apprends beaucoup.... • Publication d’articles sur mon blog • NoSQL, CloudSQL, Memcache.... • Twitter Boostrap 2 and Google Maps • Google AppEngine Full Text Search with Cloud • Utilisation de Git SQL • Installing Memcached on Mac OS X and using it in • Twitter Bootstrap, Less Java • JAX-RS: Jersey and JSON single element arrays • Create and Deploy a JAX-RS REST service on Google App Engine • .... 44
  • 65. Tribulations d'un développeur Java dans le Cloud Tugdual Grall @tgrall 46

Notes de l'éditeur

  • #2: \n
  • #3: Bas&amp;#xE9; sur MON retour d&amp;#x2019;experience: je ne suis pas un specialiste de GAE !\nJuste un developpement pour le fun\nJe passe tres peu de temps sur cette appli (de moins en moins...)\n\nExplication de mes choix et des &amp;#x201C;problemes&amp;#x201D; rencontr&amp;#xE9;s\n
  • #4: Chez eXo, Oracle... developpement Java, JavaEE, SOA, ...\nCo Fondateur en 2008 du NantesJUG\nTwitter.... et comme nous allons parler sport: runkeeper\n\nQd je ne te travaille pas, ou je ne m&amp;#x2019;occupe pas de mes enfants...\n - je m;entraine ou coure.... et quelque fois je code\n - notamment dans le train (2h en gros)\n
  • #5: Voici la structure de la presentation...\n- mes choix et besoins\n- les bonnes surprises, decouvertes (et les moins bonnes) du cloud (GAE)\n- conclusion,,,\n
  • #6: L&amp;#x2019;id&amp;#xE9;e de ce site est parti de 2 besoins:\n 1- besoin &amp;#x201C;fonctionnel&amp;#x201D; analyser les resultats, suivre les copains et mes evolutions\n 2- besoin &amp;#x201C;technique&amp;#x201D; : une application plus pouss&amp;#xE9;e qu&amp;#x2019;un simple helloworld pour plonger un peu plus dans le cloud (ou sauter)\n
  • #7: \nPetite demo...\n\nVoici quelques ecrans de mon application.\nJe ne vais pas rentrer dans les details fonctionnels pour le moment....\n\n
  • #8: Voici mes &amp;#x201C;contraintes techniques&amp;#x201D;.\nLe point le plus important :\n - un projet perso developp&amp;#xE9; en vacances.\n - Commence dans un club de vacances en italie : Pizza &amp; Java\n\nBase sur les crit&amp;#xE8;res precedents j&amp;#x2019;ai choisi GAE.\n- Heroku, RH OpenShift ne supportaient pas encore Java\n- MicroCloud pas encore lanc&amp;#xE9;\n- EC2 trop cher/complique (au sens systeme, je veux une PaaS et non pas IaaS)\n\n\n
  • #9: GAE repond bien a tous ces criteres &amp;#x201C;techniques&amp;#x201D;\nLes autres services Google m&amp;#x2019;apparaissaient egalement tres interessant a integrer...G+ aussi :)\n
  • #10: Pourquoi Pure JSP/Servlet ? \n - pourquoi pas ? manque de competence sur les nouveaux fwk de ma part (JSF, Struts, OK,, mais quel int&amp;#xE9;r&amp;#xEA;t ?)\n - je ne voulais pas partir sur une solution Grails, Gaelyk, Play!, car je veux vraiment me concentrer sur GAE lui meme.\n\nLa migraton twitter 1.x - 2... un peu lourde non ?\n
  • #11: \n
  • #12: Le choix de la technologie &amp;#x201C;web&amp;#x201D; etait simple pour moi: JSP/JSTL/Servlet/TwitterBS... avec JQuery et autres extensions.\nRestait donc le choix de la persistence.... en utilisant le NoSQL (qui etait en aout la seule base de don&amp;#xE9;e disponible)\n\ndonc....\n
  • #13: Je me suis orient&amp;#xE9; sur JDO et ... puis datastore.\nIl est clair que comme tout le reste de l&amp;#x2019;application les choix sont discutables... mais pour moi la seule partie vraiment importante de mon application est: la base de donn&amp;#xE9;e. (les donn&amp;#xE9;es &amp;#x201C;traitees&amp;#x201D;)\nOn peut considerer le reste comme jetable, si par la suite il faut que je refactorise certaine parties no pb...\nMon modele object est tres simple, seule le &amp;#x201C;volume&amp;#x201D; entre guillement pourrait etre important dans le future.\n\nDonc JDO: pour la doc, et le cote ORM (voir suite) et Datastore pour la simplicit&amp;#xE9;/flexibilit&amp;#xE9;\n
  • #14: Les donn&amp;#xE9;es sont captur&amp;#xE9;es sur des sites publics\n\n+ Import&amp;#xE9;es/transform&amp;#xE9;s en local sur le serveur de dev (Pure GAE)\n\n+ puis export&amp;#xE9; dans l&amp;#x2019;instance GAE avec l&amp;#x2019;outil suivant\n\n
  • #15: Les donn&amp;#xE9;es sont captur&amp;#xE9;es sur des sites publics\n\n+ Import&amp;#xE9;es/transform&amp;#xE9;s en local sur le serveur de dev (Pure GAE)\n\n+ puis export&amp;#xE9; dans l&amp;#x2019;instance GAE avec l&amp;#x2019;outil suivant\n\n
  • #16: Utilisation de la Datastore API pour &amp;#x201C;sortir&amp;#x201D; les donn&amp;#xE9;es de la base de dev... et la pousser vers le serveur de prod.\n
  • #17: Cet outil m&amp;#x2019;est tres utile pour importer les donn&amp;#xE9;es...\nIl est aussi utilisable pour exporter les entites depuis le serveur de prod \nIl est possible de formatter, et transformer les objets par ces scripts (python)\nNotamme export/import: csv, xml, ...\n
  • #18: Etonnemment, GAE ne support pas encore une recherche full text. \nbesoin simple : Rechercher les athletes par leur nom.\n\nPlusieurs approche possible aujourd&amp;#x2019;hui, elastic search, lucene in GAE : MAIS COMPLEX...\n\nJ ai donc choisi pour m&amp;#x2019;amuser et pour repondre au besoin d&amp;#x2019;utiliser CloudSQL (mySQL dans le cloud)\n
  • #19: Une fois le systeme en production, avec un gros nombre de data:\n - on voit rapidement le nombre de call aux API monter (meme en read only)...\n - 50 000 requetes en free... getPropety() == 1 call\n
  • #20: La gestion du cache dans mon application est relativement simple:\n- l&amp;#x2019;eviction est geree automatiquement par memcache (cela dit il est possible de pauser un timer sur le cache)\nAdministration du cache de facon globale dans Resultri:\n - &amp;#x201C;je vide certains caches&amp;#x201D; en fonction de mes operations (par exemple ajout d&amp;#x2019;une course, je vide le cache &amp;#x201C;courses&amp;#x201D; lors de la publication)\n
  • #21: \n
  • #22: \n
  • #23: \n
  • #24: \n
  • #25: \n
  • #26: \n
  • #27: \n
  • #28: \n
  • #29: \n
  • #30: \n
  • #31: \n
  • #32: \n
  • #33: je n&amp;#x2019;ai pas encore tester les &amp;#x201C;limites&amp;#x201D;\nMeilleure gestion des ressources\n
  • #34: Une bonne surprise:\n- support de l&amp;#x2019;authentification Google Account dans le serveur de test (avec support du role admin)\n- Console de dev, pour visualiser/administrer les entit&amp;#xE9;s, queues, ...\n
  • #35: Une fonctionnalit&amp;#xE9; que je trouve tres interessante:\n - la capacit&amp;#xE9; a tester avec les &amp;#x201C;services cloud&amp;#x201D; no disponible avec plusieurs etats....\n\nJe n&amp;#x2019;ai pas encore vraiment eu besoin d&amp;#x2019;utiliser cette fonctionnalit&amp;#xE9; mais vraiment &amp;#x201C;look nice&amp;#x201D;\n
  • #36: \n
  • #37: \n
  • #38: \n
  • #39: \n
  • #40: \n
  • #41: \n
  • #42: Sur cette petite animation vous pouvez voir qu&amp;#x2019;en utilisant l&amp;#x2019;outil APPCFG pour importer mes enregistrement (la moitie d&amp;#x2019;un ironman)\nj&amp;#x2019;explose le quota des Datastore write ...\nPour l&amp;#x2019;anedocte, ce matin j&amp;#x2019;ai fait le menage dans mes entities (suppression des enregistrement &amp;#x201C;session&amp;#x201D;) et cela a exploser mon budget (limit&amp;#xE9; a $1 par jour :( 0.39 front ent / 0.66 en ecriture ... je n&amp;#x2019;ai pas trop verifi&amp;#xE9; la source exacte du probleme )\n
  • #43: Que se passe-t-il ici?\nsur les images:\n - activite nulle...\n - prix eleve\n
  • #44: \n
  • #45: \n
  • #46: \n
  • #47: \n
  • #48: \n
  • #49: \n
  • #50: J&amp;#x2019;ai bcp appris, mais il me reste encore pas mal de chose a tester/faire:\n - optimisation des donn&amp;#xE9;es (volume, indexes, ..)\n - utlisation des services XMPP, mail. &amp;#x201C;pour voir&amp;#x201D;\n - utiliser les &amp;#x201C;versions&amp;#x201D; d&amp;#x2019;applications, et lesoutils de &amp;#x201C;mise a jour des donn&amp;#xE9;es)\n - FullText Search\n - MapReduce\n
  • #51: \n
  • #52: \n