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

4 069 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
  • Soyez le premier à commenter

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

×