Tribulations dun développeur Java           dans le Cloud              Tugdual Grall                @tgrall               ...
Ce que vous allez apprendre•   Retour d’experience d’un developpeur du “Dimanche”•   Comprendre “mes” choix•   Difficultés ...
About me : “Tug”•   CTO chez eXo depuis 2008•   Développeur, Product Manager chez Oracle•   Co fondateur du NantesJUG•   @...
Agenda•   Le Cloud, Pourquoi ? Comment ?•   Les surprises Bonnes et Mauvaises•   Et Alors ?                               ...
Le cloud, pourquoi?•   Besoins fonctionnels :    •   Analyser les résultats de courses :Triathlons ou autres    •   Partag...
6
Choisir une plateforme•   Aout 2011, avant mon départ en vacances•   Support du mode déconnecté•   Java & NoSQL•   Documen...
Choisir une plateforme•   Aout 2011, avant mon départ en vacances•   Support du mode déconnecté•   Java & NoSQL•   Documen...
Google AppEngine•   Google PaaS•   Languages : Java, Python and Go•   Base de données : BigTable (NoSQL) & CloudSQL (MySQL...
Resultri en quelques mots•   Pour les Geeks                  •   Pour les Sportifs    •   Pure Servlet/JSP                ...
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è...
Accès aux données?•   Mon choix : JDO & Datastore API    •   Limité aux API Google (documenté dans le SDK)    •   JDO : ca...
Resultri: Les DonnéesSites “résultats” HTML, TXT, CSV, XML                                               13
Resultri: Les DonnéesSites “résultats”           Traitements en local HTML, TXT, CSV, XML         Utilisation du GAE Dev S...
Resultri: Les DonnéesSites “résultats”           Traitements en local              Import GAE HTML, TXT, CSV, XML         ...
Dev to “Cloud” 1/2 1:   DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();                       ...
Dev to “Cloud” 2/2appcfg.py upload_data    --config_file=config.yml    --filename=ironman-south-africa.csv    --url=http:/...
Recherche Full Text•   Beta privée en Octobre...•   Solution “Resultri”    •   Utilisation de Cloud SQL                   ...
Recherche Full Text•   Beta privée en Octobre...•   Solution “Resultri”    •   Utilisation de Cloud SQL            First &...
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’AP...
Memcache           19
Les surprises ....                     20
Création de données 1:   log.info(" -- Start -- "); 2:   DatastoreService datastore = DatastoreServiceFactory.getDatastore...
Création de données 1: log.info(" -- Start -- "); 2: DatastoreService datastore = DatastoreServiceFactory.getDatastoreServ...
Datastore : Queries1:   Query q = new Query("Employee");2:   q.addSort("lastName");3:   q.addSort("firstName");4:   Prepar...
Datastore : Queries                                                                                     1:   Doe John     ...
Datastore : Queries1:   Query q = new Query("Employee");2:   q.addFilter("lastName", FilterOperator.EQUAL , "Grall");3:   ...
Datastore : Queries1:   Query q = new Query("Employee");2:   q.addFilter("lastName", FilterOperator.EQUAL , "Grall");     ...
Datastore : Queries1:   Query q = new Query("Employee");2:   q.addFilter("lastName", FilterOperator.EQUAL , "Grall");3:   ...
Datastore : Queries1:   Query q = new Query("Employee");2:   q.addFilter("lastName", FilterOperator.EQUAL , "Grall");3:   ...
Datastore : Queries1:   Query q = new Query("Employee");2:   q.addFilter("lastName", FilterOperator.EQUAL , "Grall");3:   ...
Datastore : Queries1:   PersistenceManager pm = PMF.get().getPersistenceManager();2:   Query q = pm.newQuery(Employee.clas...
Datastore : Queries1:   PersistenceManager pm = PMF.get().getPersistenceManager();2:   Query q = pm.newQuery(Employee.clas...
Datastore : Queries1:   PersistenceManager pm = PMF.get().getPersistenceManager();2:   Query q = pm.newQuery(Employee.clas...
Datastore : Queries1:   PersistenceManager pm = PMF.get().getPersistenceManager();2:   Query q = pm.newQuery(Employee.clas...
Datastore : Queries 1: PersistenceManager pm = PMF.get().getPersistenceManager();org.datanucleus.store.appengine.query.Dat...
Manipulation des données•   Les données ne sont pas toujours disponibles    •   High Data Replication (HDR) : surprenant p...
Autres services                  28
Task Queues•   Service pour l’execution de taches en arrière plan    •    Possibilité d’inclure les taches dans un ordonna...
Test en Local•   Un version de Jetty avec le SDK Google AppEngineSimulation “Authentification Google”                    Co...
Disponibilité des services                             31
Authentification Google       •   Utilisation de Google Account           •   Se base sur la sécurité JavaEE standard      ...
URL Fetch•   Accès à des serveurs autres que AppEngine par HTTP/HTTPS    •    Utilisation de java.net.URL ou         com.g...
Accès aux API Google•   Google offre de nombreux Services et API    •   Un seul compte => tous les services               ...
Resultri & Google•   Les services que j’utilise pour Resultri    •   Google Apps : Gestion du nom de domaines, Mail, ...  ...
En production                36
Gratuit ?•   Oui mais....    •   Avec des quotas    •   Les règles peuvent changer        •   Septembre : Google modifie se...
Gratuit ?•   Oui mais....    •   Avec des quotas    •   Les règles peuvent changer        •   Septembre : Google modifie se...
Exemple          38
ExempleImport CSV             38
Gestion du cout•   En Fevrier : Augmentation du cout    •   Pas d’activité utilisateur, mais cout en augmentation         ...
Gestion du cout•   En Fevrier : Augmentation du cout    •   Pas d’activité utilisateur, mais cout en augmentation•   Une i...
Merci Google!•   Google Bots, et autres moteurs de recherche    •   Utilisation de Google Webmaster Tools    •   Mise en p...
Merci Google!•   Google Bots, et autres moteurs de recherche                                                  User-agent: ...
Merci Google!•   Google Bots, et autres moteurs de recherche                                                  User-agent: ...
Console          41
Console          41
Console          41
Conclusion             42
Conclusion•   AppEngine est-il le bon choix pour Resultri?    •   Pour l’instant trop couteux et “puissant”    •   Techniq...
Conclusion•   Techniquement : j’apprends beaucoup....   •   Publication d’articles sur mon blog    •   NoSQL, CloudSQL, Me...
Questions?             45
Tribulations dun développeur Java           dans le Cloud              Tugdual Grall                @tgrall               ...
Prochain SlideShare
Chargement dans…5
×

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

3 945 vues

Publié le

Comme beaucoup de développeurs une grande partie de mon temps libre est utilisé pour découvrir de nouvelles technologies et développer des applications avec celles-ci.
J'ai donc choisi de découvrir le développement d'application Java sur le cloud, avec Google AppEngine, pour créer le site http://www.resultri.com qui permet de gérer les resultats de triathlon (mon autre passion).
Développer cette application est une aventure interessante que je partage avec vous durant ce BOF:

découverte de GAE et des outils de developpement
les "surprises" du NoSQL, surtout pour un cerveau "cablé relationnel comme le mien"
hmmm tout n'est pas gratuit?
les quelques trucs à savoir : l'importance de memcache, utilisation de CloudSQL, les batchs....

Publié dans : Technologie
0 commentaire
2 j’aime
Statistiques
Remarques
  • Soyez le premier à commenter

Aucun téléchargement
Vues
Nombre de vues
3 945
Sur SlideShare
0
Issues des intégrations
0
Intégrations
302
Actions
Partages
0
Téléchargements
0
Commentaires
0
J’aime
2
Intégrations 0
Aucune incorporation

Aucune remarque pour cette diapositive
  • \n
  • Basé sur MON retour d’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 “problemes” rencontrés\n
  • 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’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
  • 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
  • L’idée de ce site est parti de 2 besoins:\n 1- besoin “fonctionnel” analyser les resultats, suivre les copains et mes evolutions\n 2- besoin “technique” : une application plus poussée qu’un simple helloworld pour plonger un peu plus dans le cloud (ou sauter)\n
  • \nPetite demo...\n\nVoici quelques ecrans de mon application.\nJe ne vais pas rentrer dans les details fonctionnels pour le moment....\n\n
  • Voici mes “contraintes techniques”.\nLe point le plus important :\n - un projet perso developpé en vacances.\n - Commence dans un club de vacances en italie : Pizza & Java\n\nBase sur les critères precedents j’ai choisi GAE.\n- Heroku, RH OpenShift ne supportaient pas encore Java\n- MicroCloud pas encore lancé\n- EC2 trop cher/complique (au sens systeme, je veux une PaaS et non pas IaaS)\n\n\n
  • GAE repond bien a tous ces criteres “techniques”\nLes autres services Google m’apparaissaient egalement tres interessant a integrer...G+ aussi :)\n
  • Pourquoi Pure JSP/Servlet ? \n - pourquoi pas ? manque de competence sur les nouveaux fwk de ma part (JSF, Struts, OK,, mais quel intérê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
  • \n
  • Le choix de la technologie “web” 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ée disponible)\n\ndonc....\n
  • Je me suis orienté sur JDO et ... puis datastore.\nIl est clair que comme tout le reste de l’application les choix sont discutables... mais pour moi la seule partie vraiment importante de mon application est: la base de donnée. (les données “traitees”)\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 “volume” 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é/flexibilité\n
  • Les données sont capturées sur des sites publics\n\n+ Importées/transformés en local sur le serveur de dev (Pure GAE)\n\n+ puis exporté dans l’instance GAE avec l’outil suivant\n\n
  • Les données sont capturées sur des sites publics\n\n+ Importées/transformés en local sur le serveur de dev (Pure GAE)\n\n+ puis exporté dans l’instance GAE avec l’outil suivant\n\n
  • Utilisation de la Datastore API pour “sortir” les données de la base de dev... et la pousser vers le serveur de prod.\n
  • Cet outil m’est tres utile pour importer les donné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
  • Etonnemment, GAE ne support pas encore une recherche full text. \nbesoin simple : Rechercher les athletes par leur nom.\n\nPlusieurs approche possible aujourd’hui, elastic search, lucene in GAE : MAIS COMPLEX...\n\nJ ai donc choisi pour m’amuser et pour repondre au besoin d’utiliser CloudSQL (mySQL dans le cloud)\n
  • 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
  • La gestion du cache dans mon application est relativement simple:\n- l’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 - “je vide certains caches” en fonction de mes operations (par exemple ajout d’une course, je vide le cache “courses” lors de la publication)\n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • je n’ai pas encore tester les “limites”\nMeilleure gestion des ressources\n
  • Une bonne surprise:\n- support de l’authentification Google Account dans le serveur de test (avec support du role admin)\n- Console de dev, pour visualiser/administrer les entités, queues, ...\n
  • Une fonctionnalité que je trouve tres interessante:\n - la capacité a tester avec les “services cloud” no disponible avec plusieurs etats....\n\nJe n’ai pas encore vraiment eu besoin d’utiliser cette fonctionnalité mais vraiment “look nice”\n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • Sur cette petite animation vous pouvez voir qu’en utilisant l’outil APPCFG pour importer mes enregistrement (la moitie d’un ironman)\nj’explose le quota des Datastore write ...\nPour l’anedocte, ce matin j’ai fait le menage dans mes entities (suppression des enregistrement “session”) et cela a exploser mon budget (limité a $1 par jour :( 0.39 front ent / 0.66 en ecriture ... je n’ai pas trop verifié la source exacte du probleme )\n
  • Que se passe-t-il ici?\nsur les images:\n - activite nulle...\n - prix eleve\n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • J’ai bcp appris, mais il me reste encore pas mal de chose a tester/faire:\n - optimisation des données (volume, indexes, ..)\n - utlisation des services XMPP, mail. “pour voir”\n - utiliser les “versions” d’applications, et lesoutils de “mise a jour des données)\n - FullText Search\n - MapReduce\n
  • \n
  • \n
  • Devoxx: Tribulation d'un développeur sur le Cloud

    1. 1. Tribulations dun développeur Java dans le Cloud Tugdual Grall @tgrall 1
    2. 2. Ce que vous allez apprendre• Retour d’experience d’un developpeur du “Dimanche”• Comprendre “mes” choix• Difficultés Surprises du Cloud 2
    3. 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. 4. Agenda• Le Cloud, Pourquoi ? Comment ?• Les surprises Bonnes et Mauvaises• Et Alors ? 4
    5. 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. 6
    7. 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. 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. 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. 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
    11. 11. Traitement des données 10
    12. 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. 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. 14. Resultri: Les DonnéesSites “résultats” HTML, TXT, CSV, XML 13
    15. 15. Resultri: Les DonnéesSites “résultats” Traitements en local HTML, TXT, CSV, XML Utilisation du GAE Dev Server Export CSV 13
    16. 16. Resultri: Les DonnéesSites “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. 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 CSV13: 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 Datastore17: ! ! Date date = (Date) o; •18: ! ! printValue = formatter.format(date);19:20: ! } } else if ... { ...!} Trop “couteux” à faire sur le Cloud21: out.print((printValue == null)?"":printValue);22: out.print(",");23: ! }24: ... 14
    18. 18. Dev to “Cloud” 2/2appcfg.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=TriathlonResultUploading 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_apiPlease enter login credentials for dev-result-db.appspot.com Import des données “formatées”Email: tugdual@gmail.comPassword 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
    19. 19. Recherche Full Text• Beta privée en Octobre...• Solution “Resultri” • Utilisation de Cloud SQL 16
    20. 20. Recherche Full Text• Beta privée en Octobre...• Solution “Resultri” • Utilisation de Cloud SQL First & Last NamesBigTable CloudSQL 16
    21. 21. Accès aux données• Attention aux quotas ! 17
    22. 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. 23. Memcache 19
    24. 24. Les surprises .... 20
    25. 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. 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 doGet2: 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 Martignole10: employee = new Entity("Employee");5: Apr 16, 2012 4:22:25 "firstName", "Antonio");11: employee.setProperty( PM com....Servlet doGet6: INFO: -- setProperty("lastName", "Goncalvez");12: employee. End --7: Apr 16, 2012 4:Date(); com.......$PersistDatastore persist13: hireDate = new 22:34 PM8: INFO: Time to persist"hireDate",: hireDate);14: employee.setProperty( datastore 4 ms15: 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. 27. Datastore : Queries1: 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. 28. Datastore : Queries 1: Doe John 2: Goncalvez Antonio1: Query q = new Query("Employee"); 3: Grall Briac2: q.addSort("lastName"); 4: Grall Corentin3: q.addSort("firstName"); 5: Grall Malo4: PreparedQuery pq = datastore.prepare(q); 6: Grall Nolwenn5: for (Entity emp : pq.asIterable()) { 7: Grall Tug 8: Grall Virginie6: o.println(emp.getProperty("lastName") +" "+ emp.getProperty("firstName") ); 9: Martignole Nicolas7: } 10: Tabarly Eric 22
    29. 29. Datastore : Queries1: 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. 30. Datastore : Queries1: Query q = new Query("Employee");2: q.addFilter("lastName", FilterOperator.EQUAL , "Grall"); 1: Grall Briac3: q.addSort("lastName"); 2: Grall Corentin4: q.addSort("firstName"); 3: Grall Malo5: PreparedQuery pq = datastore.prepare(q); 4: Grall Nolwenn6: for (Entity emp : pq.asIterable()) { 5: Grall Tug 6: Grall Virginie7: o.println(emp.getProperty("lastName") + " "+ emp.getProperty("firstName") );8: } 23
    31. 31. Datastore : Queries1: 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. 32. Datastore : Queries1: 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. 33. Datastore : Queries1: 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. 34. Datastore : Queries1: 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. 35. Datastore : Queries1: 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 Malo4: o.println(results); 25
    36. 36. Datastore : Queries1: 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. 37. Datastore : Queries1: 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. 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. 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
    40. 40. Autres services 28
    41. 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. 42. Test en Local• Un version de Jetty avec le SDK Google AppEngineSimulation “Authentification Google” Console Developpeur 30
    43. 43. Disponibilité des services 31
    44. 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. 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. 46. Accès aux API Google• Google offre de nombreux Services et API • Un seul compte => tous les services 34
    47. 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
    48. 48. En production 36
    49. 49. Gratuit ?• Oui mais.... • Avec des quotas • Les règles peuvent changer • Septembre : Google modifie ses tarifs 37
    50. 50. Gratuit ?• Oui mais.... • Avec des quotas • Les règles peuvent changer • Septembre : Google modifie ses tarifs 37
    51. 51. Exemple 38
    52. 52. ExempleImport CSV 38
    53. 53. Gestion du cout• En Fevrier : Augmentation du cout • Pas d’activité utilisateur, mais cout en augmentation 39
    54. 54. Gestion du cout• En Fevrier : Augmentation du cout • Pas d’activité utilisateur, mais cout en augmentation• Une idée ? 39
    55. 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. 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. 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. 58. Console 41
    59. 59. Console 41
    60. 60. Console 41
    61. 61. Conclusion 42
    62. 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. 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
    64. 64. Questions? 45
    65. 65. Tribulations dun développeur Java dans le Cloud Tugdual Grall @tgrall 46

    ×