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....
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
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
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: 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
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
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
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
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
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
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
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
Bas&#xE9; sur MON retour d&#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 &#x201C;problemes&#x201D; rencontr&#xE9;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&#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
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&#x2019;id&#xE9;e de ce site est parti de 2 besoins:\n 1- besoin &#x201C;fonctionnel&#x201D; analyser les resultats, suivre les copains et mes evolutions\n 2- besoin &#x201C;technique&#x201D; : une application plus pouss&#xE9;e qu&#x2019;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 &#x201C;contraintes techniques&#x201D;.\nLe point le plus important :\n - un projet perso developp&#xE9; en vacances.\n - Commence dans un club de vacances en italie : Pizza & Java\n\nBase sur les crit&#xE8;res precedents j&#x2019;ai choisi GAE.\n- Heroku, RH OpenShift ne supportaient pas encore Java\n- MicroCloud pas encore lanc&#xE9;\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 &#x201C;techniques&#x201D;\nLes autres services Google m&#x2019;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&#xE9;r&#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
\n
Le choix de la technologie &#x201C;web&#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&#xE9;e disponible)\n\ndonc....\n
Je me suis orient&#xE9; sur JDO et ... puis datastore.\nIl est clair que comme tout le reste de l&#x2019;application les choix sont discutables... mais pour moi la seule partie vraiment importante de mon application est: la base de donn&#xE9;e. (les donn&#xE9;es &#x201C;traitees&#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 &#x201C;volume&#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&#xE9;/flexibilit&#xE9;\n
Les donn&#xE9;es sont captur&#xE9;es sur des sites publics\n\n+ Import&#xE9;es/transform&#xE9;s en local sur le serveur de dev (Pure GAE)\n\n+ puis export&#xE9; dans l&#x2019;instance GAE avec l&#x2019;outil suivant\n\n
Les donn&#xE9;es sont captur&#xE9;es sur des sites publics\n\n+ Import&#xE9;es/transform&#xE9;s en local sur le serveur de dev (Pure GAE)\n\n+ puis export&#xE9; dans l&#x2019;instance GAE avec l&#x2019;outil suivant\n\n
Utilisation de la Datastore API pour &#x201C;sortir&#x201D; les donn&#xE9;es de la base de dev... et la pousser vers le serveur de prod.\n
Cet outil m&#x2019;est tres utile pour importer les donn&#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
Etonnemment, GAE ne support pas encore une recherche full text. \nbesoin simple : Rechercher les athletes par leur nom.\n\nPlusieurs approche possible aujourd&#x2019;hui, elastic search, lucene in GAE : MAIS COMPLEX...\n\nJ ai donc choisi pour m&#x2019;amuser et pour repondre au besoin d&#x2019;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&#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 - &#x201C;je vide certains caches&#x201D; en fonction de mes operations (par exemple ajout d&#x2019;une course, je vide le cache &#x201C;courses&#x201D; lors de la publication)\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
je n&#x2019;ai pas encore tester les &#x201C;limites&#x201D;\nMeilleure gestion des ressources\n
Une bonne surprise:\n- support de l&#x2019;authentification Google Account dans le serveur de test (avec support du role admin)\n- Console de dev, pour visualiser/administrer les entit&#xE9;s, queues, ...\n
Une fonctionnalit&#xE9; que je trouve tres interessante:\n - la capacit&#xE9; a tester avec les &#x201C;services cloud&#x201D; no disponible avec plusieurs etats....\n\nJe n&#x2019;ai pas encore vraiment eu besoin d&#x2019;utiliser cette fonctionnalit&#xE9; mais vraiment &#x201C;look nice&#x201D;\n
\n
\n
\n
\n
\n
\n
Sur cette petite animation vous pouvez voir qu&#x2019;en utilisant l&#x2019;outil APPCFG pour importer mes enregistrement (la moitie d&#x2019;un ironman)\nj&#x2019;explose le quota des Datastore write ...\nPour l&#x2019;anedocte, ce matin j&#x2019;ai fait le menage dans mes entities (suppression des enregistrement &#x201C;session&#x201D;) et cela a exploser mon budget (limit&#xE9; a $1 par jour :( 0.39 front ent / 0.66 en ecriture ... je n&#x2019;ai pas trop verifi&#xE9; 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&#x2019;ai bcp appris, mais il me reste encore pas mal de chose a tester/faire:\n - optimisation des donn&#xE9;es (volume, indexes, ..)\n - utlisation des services XMPP, mail. &#x201C;pour voir&#x201D;\n - utiliser les &#x201C;versions&#x201D; d&#x2019;applications, et lesoutils de &#x201C;mise a jour des donn&#xE9;es)\n - FullText Search\n - MapReduce\n