2. Objectifs de la formation
2
• Se familiariser avec les services de GAE et son DataStore
• Savoir les contraintes liées au développement sur GAE
• Développer et déployer en Live des applications Web sur
l’infrastructure GAE
• Avoir suffisamment de connaissance pour passer à la vitesse
supérieure
3. Sommaire
3
I. Présentation générale
II. Services offerts par GAE
III. Persistance dans GAE
IV. Web services Rest dans GAE
* Code Lab après chaque partie
4. A propos de moi
4
• RHANIZAR Abdelhakim
• Ingénieur d’état en informatique
• Spécialiste du Cloud Computing & RIA
a.rhanizar@gmail.com
@a_rhanizar
6. Google App Engine
6
• Plateforme (PaaS) pour développer et déployer des applications
Web sur l’infrastructure de Google
• Support des RunTime Java (JRuby, Groovy, Scala...), Python et Go
• Réutilisation de la plupart des API -Java- standards (Servlet/JSP ,
Mail, JPA, JDO..) et des frameworks populaires (Struts 2, Spring
MVC, …)
• Quotas gratuits puis Pay-As-You-Go
7. GAE: Avantages
7
• Infrastructure haut de gamme de Google
• Flexibilité/Scalabilité automatique
• Réduction du Time to marquet
• Plus d’agilité
• Quota gratuit généreux
• Pay per Use
• 99.95 SLA
8. Etat de santé de GAE
8
• http://code.google.com/status/appengine -> A suivre!
• google-appengine-downtime-notify@googlegroups.com -> A adhérer!
10. Sandbox
10
• Environnement qui isole l’exécution des applications GAE et facilite
leur sclabilité pour Google
• Plusieurs restrictions imposées sur les threads
• Une application GAE ne peut pas:
Ecrire dans un fichier système (java.io.FileWriter)
Accéder à un fichier qui n’a pas créé
Communiquer avec l’extérieur sans passer par les API de GAE
(Sockets, HttpClient)
Faire des appels système (java.lang.System)
• Liste des classes Java autorisées:
https://developers.google.com/appengine/docs/java/jrewhitelist
11. Frontend Vs Backend instances
11
Fonctionnalité Frontend Backend
Limites 60 sec maxi par requête HTTP 100 API call simultanés par requête HTTP
10 min maxi par tâche/job 24h maxi / job
CPU Flexible, facturé à l’usage Configurable 400 MHz – 4.8 GHz
Mémoire 128 Mb Configurable 128 Mb – 1 Gb
Volatilité Instances Configurable ( Résident + dynamique )
volatiles/dynamiques
Accessibilité Instance anonyme Accessible via une URL
http://instance.backend_name.your_app_id.appspot.com
Scalabilité Automatique (GAE Scheduler) Configurable
Requêtes Off par défaut, 10 maxi Oui, le nombre de requêtes maxi est
simultanées configurable
12. Prix des instances Backend
12
Tarif horaire Tarif mensuel
Classe RAM CPU
d’une instance d’une instance
B1 128MB 600MHz $0.08 $57.60
B2 (Par défaut) 256MB 1.2GHz $0.16 $115.20
B4 512MB 2.4GHz $0.32 $230.40
B8 1024MB 4.8GHz $0.64 $460.80
Avez-vous vraiment besoin d’utiliser des instances Backend?
Bonne pratique: Exécuter les traitements lourds dans des tâches
(queue) -> 10 min/tâche au lieu de 1min/requête!
13. Le scheduler GAE
13
• C’est un service qui décide comment traiter une requête d’un utilisateur:
1. La faire servir par une instance Idle
2. Lancer une nouvelle instance pour la servir
3. Attendre la libération d’une instance existante Instances occupées
Client 1
File d’attente
Requête HTTP R2 R2 R1
Client 2 Scheduler
Client 3
Une application non optimisée -> temps d’attente important
-> UX dégradée + argent dépensé dans le CPU Instances fraîches
15. Code Lab N1
15
Pré-requis:
• Eclipse JEE Edition
• JDK 6
• SDK GAE pour Java
• Version JEE de Eclipse
• Google plugin pour Eclipse
• Un compte Gmail
• Un téléphone mobile
Objectifs:
• Créer votre première application sur GAE
• Déploiement en Live
• Comprendre le cycle de développement sur GAE
• Se familiariser avec la console d’administration
• Instructions dans le fichier lab1.pdf
19. MemCache
19
• Cache distribué pour stocker les données
• Deux modes d’écriture: synchrone et asynchrone
• Mémoire volatile
• Taille maximale de 1Mb par objet stocké
String key = ‘email’;
User user =null;
// Using the synchronous cache
MemcacheService syncCache = MemcacheServiceFactory.getMemcacheService();
user = (User) syncCache.get(key); // read from cache
if (user == null) {
// get user from other source (Date Store)
// ........
syncCache.put(key, user, Expiration.byDeltaSeconds(60)); // populate cache
}
Bonne pratique: 1- Memcache est un service à utiliser sans
modération afin de soulager le DateStore
2- Faire des insertions en Batch -> cache.putAll(values)
20. Files d’attente: Task Queue
20
• Effectuer un traitement en tâche de fond en dehors des requêtes
des utilisateurs
• Organiser le travail en petites unités discrètes appelées tâches
• Ces tâches sont mises dans une ou plusieurs files d'attente
• Elles sont exécutées lorsque les ressources système le permettent
Caractéristiques Push Queue Pull Queue
Scalabilité Automatique par GAE A votre charge
(workers backend)
Gestion des tâches Automatique A votre charge
(suppression,…etc)
Accès par les tiers non Oui (Rest API)
Quota:
Taille maxi / tâche 100 Kb 1 Mb
Nombre max de queues actives 10 Free, 100 app payante 10 Free, 100 app payante
Taux d’exécution maxi 500/sec/queue Personnalisable
22. Url Fetch
22
• API pour communiquer avec d’autres applications ou accéder à
d’autres ressources sur le Web
• Support du protocole HTTP (POST, GET, PUT,…)
• Appels en synchrone et asynchrone
Restriction Quantité
Pour savoir plus sur les quotas du service Url Fetch
taille d'une requête 1 Mo
https://developers.google.com/appengine/docs/quotas#UrlFetch
taille d'une réponse 1 Mo
try {
URL url = new URL("http://www.example.com/atom.xml");
BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream()));
String line;
while ((line = reader.readLine()) != null) {
// ...
}
reader.close();
} catch (Exception e) {
// ...
}
23. Blobstore
23
• Service capable de servir et recevoir des fichiers importants souvent
via un formulaire d’upload
• Possibilité d’écrire dynamiquement des blobs
• Intégration native avec l’API de conversion
Restriction Quantité
Taille d'un Blob 32 Mo
Taille totale des blobs 5 Gb
BlobstoreService blobstoreService = BlobstoreServiceFactory.getBlobstoreService();
public void doGet(HttpServletRequest req, HttpServletResponse res)
throws IOException {
BlobKey blobKey = new BlobKey(req.getParameter("blob-key"));
// servir un fichier blob
blobstoreService.serve(blobKey, res);
}
}
24. Mail API
24
• Envoyer des email au nom de l’administrateur
• Recevoir des emails dans l’application
Ressource Daily Limit Maximum Rate
Mail API Calls 100 calls 32 calls/minute
Messages Sent 100 messages 8 messages/minute
Admins Emailed 5,000 mails 24 mails/minute
Message Body Data Sent 60 MB 340 KB/minute
Attachments Sent 2,000 attachments 8 attachments/minute
Attachment Data Sent 100 MB 10 MB/minute
Properties props = new Properties();
Session session = Session.getDefaultInstance(props, null);
String msgBody = "...";
Message msg = new MimeMessage(session);
msg.setFrom(new InternetAddress("admin@example.com", "Example.com Admin"));
msg.addRecipient(Message.RecipientType.TO, new InternetAddress("user@example.com", "Mr. User"));
msg.setSubject("Your Example.com account has been activated");
msg.setText(msgBody);
Transport.send(msg);
25. Tâches planifiées (cron)
25
• Configurer des tâches planifiées qui s'exécutent à des heures
définies ou à intervalles réguliers
• Très utile pour l’envoi des rapports, rafraichir des éléments dans le
cache ..etc
<cronentries>
<cron>
<url>/recache</url>
<description>Repopulate the cache every 2 minutes</description>
<schedule>every 2 minutes</schedule>
</cron>
<cron>
<url>/weeklyreport</url>
<description>Mail out a weekly report</description>
<schedule>every monday 08:30</schedule>
<timezone>America/New_York</timezone>
</cron>
</cronentries>
26. AppStats
26
• Maitriser l’utilisation des Services GAE
• Optimiser les requêtes internes et externes
27. Code Lab N2
27
Objectifs:
• Initiation à l’utilisation des services clés de GAE
• Savoir optimiser son code grâce à AppStats
• Maitriser le temps d’exécution des requêtes (Backend performance)
• Instructions dans le fichier lab2.pdf
29. Options offertes
29
• App Engine DataStore
• Google Cloud SQL
• Google Cloud Storage
• Garder vos données chez vous!
30. App Engine Datastore
30
• Base de données non relationnelle de type clé – valeur basée sur
BigTable
• Architecture distribuée
• Scalabilité et réplication automatique
• Beaucoup de différences avec les RDBMS traditionnels:
– Pas de schéma
– Requêtes pré-indexées
– Pas de d’opération Join
– Pas de fonctions d’agrégation
• Accès via:
– Standards Java: JDO/JPA, Hibernate
– Low-level API
– Librairies tierces: Objectify, Twig,..
31. GAE Datastore: vocabulaire
31
• Entity: Objet contenant des données stockées dans le datastore
– Deux entités du même type peuvent avoir des propriétés différentes
– Une application GAE ne peut accéder qu’aux entités qu’elle a créé
• Key: Propriété qui identifie une entité dans le datastore
– Une entité est identifiable par trois propriétés:
• Kind: type de l’entité (nom de la classe)
• Id: identifiant de l’entité
• Parent: Option pour définir l’emplacement de stockage d’une entité dans le
datastore
– Une entité sans parent est dite entité root
• Entity group: Une entité, ses parents et ses fils (ancestor path)
appartiennent à la même entity group
• Index: Propriété qui permet d’exécuter une requête sur le datastore
• Transaction: Une opération, ou un ensemble d’opérations
atomiques -> indivisibles
32. Gestion d’accès
32
Version
1.0
Accès direct
App 1
Datastore 1 Version
1.0.6
Version
Test
Remote API
App 2
App 3
33. Autres limitations
33
• On ne peut pas exécuter une requête sur une propriété non indexée
• Un index met du temps pour être disponible (mis à jour)
• Le parent d’une entité est permanent
• Les requêtes sont limitées à un seul groupe d’entité (Entity group)
• Un seul filtre d’inégalité est autorisé
Resource Free Default Daily Limit Billing Enabled Default Limit
Stored Data 1 GB Note: Not a daily limit but a total limit. 1 GB free; no maximum
Number of Indexes 200 Note: Not a daily limit but a total limit. 200
Write Operations 50,000 Unlimited
Read Operations 50,000 Unlimited
Maximum entity size 1 Mb 1Mb
Maximum transaction size 10 Mb 10Mb
34. Objectify
34
• Bibliothèque qui vous permet de persister des objets Java en toute
simplicité
• Forte abstraction des API du Datastore
• Quatre opérations basiques GET, PUT, DELETE, QUERY
• Annotations Java
• Système de cache qui se base sur le service MemCache & sessions
public class Car{
@Id Long id;
@Unindexed String prop;
int color;
@Transient String doNotPersist;
private Car() {} // mandatory no-arg constructor
public Car(String prop, int color) {
this. prop = prop;
this.color = color;
}
}
35. Objectify en pratique
35
// Vous devez enregistrer vos entités Class
ObjectifyService.register(Car.class);
ObjectifyService.register(Motorcycle.class);
Objectify ofy = ObjectifyService.begin();
// Simple create
Car porsche = new Car("2FAST", "red");
ofy.put(porsche);
assert porsche.id != null; // id was autogenerated
// Get it back
Car fetched1 = ofy.get(new Key<Car>(Car.class, porsche.id));
Car fetched2 = ofy.get(Car.class, porsche.id); // equivalent, more
convenient
assert areEqual(porsche, fetched1, fetched2);
// Change some data and write it
porsche.color = "blue";
ofy.put(porsche);
// Delete it
ofy.delete(porsche);
36. Objectify: opérations en batch
36
Objectify ofy = ObjectifyService.begin();
// Create
Car porsche = new Car("2FAST", "red");
Car unimog = new Car("2SLOW", "green");
Car tesla = new Car("2NEW", "blue");
ofy.put(tesla, unimog, porsche); //varargs; Car[] and Iterable<Car> also work
// More convenient shorthand, note the return type
Map<Long, Car> fetched = ofy.get(Car.class, new Long[] { porsche.id,
unimog.id, tesla.id });
// Delete the data
ofy.delete(fetched.values());
// You can delete by key without loading the objects
ofy.delete(
new Key<Car>(Car.class, porsche.id),
new Key<Car>(Car.class, unimog.id),
new Key<Car>(Car.class, tesla.id));
37. Objectify: les requêtes
37
Objectify ofy = ObjectifyService.begin();
Car car = ofy.query(Car.class).filter("prop", "123456789").get();
// The Query itself is Iterable
Query<Car> q = ofy.query(Car.class).filter("prop >", "123456789").order("-prop");
for (Car car: q) {
System.out.println(car.toString());
}
// You can query for just keys, which will return Key objects much more efficiently
than fetching whole objects
Iterable<Key<Car>> allKeys = ofy.query(Car.class).fetchKeys();
// Useful for deleting items
ofy.delete(allKeys);
38. Objectify: Polymorphisme
38
• L’entité root doit porter l’annotation @Entity
• Les classes polymorphes doivent porter l’annotation @Subclass
• Une modification dans la hiérarchie des classes nécessite une mise
à jour des index
@Entity
public class Animal {
@Id Long id;
String name;
}
@Subclass
public class Mammal extends Animal {
boolean longHair;
}
@Subclass
public class Cat extends Mammal {
boolean hypoallergenic;
}
39. Objectify: Polymorphisme - suite
39
Objectify ofy = ObjectifyService.begin();
Animal annie = new Animal();
annie.name = "Annie";
ofy.put(annie);
Mammal mam = new Mammal();
mam.name = "Mam";
m.longHair = true;
ofy.put(mam);
Cat minou= new Cat();
minou.name = "Minou";
minou.longHair = true;
ofy.put(minou);
// This will return the Cat
Animal fetched = ofy.get(Animal.class, minou.id);
// This query will produce three objects, the Animal, Mammal, and Cat
Query<Animal> all = ofy.query(Animal.class);
// This query will produce the Mammal and Cat
Query<Mammal> mammals = ofy.query(Mammal.class);
40. Les transactions
40
• Une transaction peut manipuler des données de différents Entity
Group
• 5 Entity Group maxi
Objectify ofy = ObjectifyService.beginTransaction(); // instead of begin()
try
{
ClubMembers cm = ofy.get(ClubMembers.class, "k123");
cm.incrementByOne();
ofy.put(cm);
ofy.getTxn().commit();
}
finally
{
if (ofy.getTxn().isActive())
ofy.getTxn().rollback();
}
41. Les relations entre le objets
41
• Relation Parent
– Les classes Person et Car appartiennent au même Entity Group
– Modifier le owner créera une nouvelle entité mais ne supprimera pas l’ancienne!
– La relation parent est permanente
public class Person {
@Id Long id;
String name;
}
public class Car {
@Id Long id;
@Parent Key<Person> owner;
String color;
}
Objectify ofy = ObjectifyService.begin();
Key<Person> owner = new Key<Person>(Person.class, somePersonId);
Car someCar = ofy.get(new Key<Car>(owner, Car.class, someCarId));
42. Relation Single-Value
42
• Dans une relation single-value, une clé une propriété comme les
autres propriétés de l’entité
- One to one - Many to One
public class Person { public class Employee
@Id String name; {
Key<Person> significantOther; @Id String name;
} Key<Employee> manager;
Objectify ofy = ObjectifyService.begin(); }
Person bob = ofy.get(Person, "bob");
Person bobswife = Objectify ofy = ObjectifyService.begin();
ofy.get(bob.significantOther); Employee bob = ofy.get(Employee.class, "bob");
Employee fred = ofy.get(bob.manager);
Iterable<Employee> subordinates =
ofy.query(Employee.class).filter("manager", fred);
43. Relation Multi-value
43
public class Employee{
@Id String name;
Key<Employee>[] subordinates;
}
Objectify ofy = ObjectifyService.begin();
// should contain Fred
Iterable<Employee> managers = ofy.query(Employee.class).filter("subordinates", bob);
• Le Datastore peut aussi persister des collections et des tableaux
• 5000 sous entités au maximum
• Chaque get() ou put() chargera/mettra à jour les clés de toutes les
sous entités (performance)
44. Statistiques sur vos données
44
Accès dynamique en java: https://developers.google.com/appengine/docs/java/datastore/stats
45. Code Lab N3
45
Objectifs:
• Initiation à la persistance des données dans le Datastore de GAE grâce à la
bibliothèque Objectify
• Inspecter les interactions avec le Datastore grâce au service AppStats
• Instructions dans le fichier lab3.pdf
47. Restlet
47
• Framework Java pour développer des API REST réutilisables sur
d’autres plateformes (Android, GWT,…)
• Support de plusieurs représentations (XML,JSON,JAXB,…)
• Fonctionnalités avancées pour le rooting et la sécurité
• Négociation de contenu