Presentation Layer Patterns Session Management Play FRamework

Werbung
PLAY FRAMEWORK
Webnesday #5
Mirko Stocker
Institut für Software
Rapperswil, 13. Mai, 2015
Mirko Stocker
 InfoQ Editor
 Wissenschaftlicher Mitarbeiter
 Application Architecture
 Scala, Ruby
 Cloud Solutions
 www.infoq.com/author/Mirko-Stocker
 Parallele Programmierung
 Web-Entwicklung
 User Interfaces
 www.burg-au.ch
 appquest.hsr.ch
 www.free-enterprise.ch
 Product Manager
 Consulting und Projekt-Begleitung
 Eclipse Plug-ins für C++/Scala
 Forschungsprojekte
github.com/misto | misto.ch | @m_st | facebook.com/misto | ch.linkedin.com/in/misto
Seite 2
© Mirko Stocker 2014
Play Framework
Seite 3
© Mirko Stocker 2014
Warum Play?
 JVM, aber kein JEE/Servlet
 HTTP Interface, non-blocking I/O
 Typsicherheit: Templates, URLs
 Entwicklungszyklus: Edit -> Refresh
Seite 4
© Mirko Stocker 2014
Grundlagen des Webs
HTTP
Back
Reload
URLs
Seite 5
© Mirko Stocker 2014
Grundlagen des Webs – Bezogen auf Play
HTTP nicht verstecken
Stateless
Reload
URLs sprechen
 Stateless: Der Back-Button funktioniert
 HTTP ist nicht nur ein Transport für RPC (REST Maturity Model)
 URLs sollten sprechen (Level 1, Links zu verschiedenen Ressourcen)
 Reload: Als Entwickler will ich Änderungen sofort sehen
Seite 6
© Mirko Stocker 2014
Play ist Stateless
 Kein Zustand im Applikations Tier
 Im Unterschied zu Java Servlet HttpSession
 Zustand ist also eine Kombination aus
 Client Session State
 Database Session State
 Vorteile
 Einfach horizontal skalierbar, da egal ist, welche Instanz einen Request
behandelt (Cloud-Ready)
 Kein Synchronisationsaufwand in der Applikation
 Entwicklung und Test vereinfacht, da Verhalten reproduzierbar ist
Seite 8
© Mirko Stocker 2014
Play macht dem Entwickler Spass
 Play kommt ohne Applikationsserver aus
 Basiert auf Netty
 Kann aber auch als WAR deployed werden
 Kein Deployment während der Entwicklung
 Beim Reload werden die geänderten Klassen und Templates neu kompiliert
und geladen
 Gute Fehlermeldungen direkt im Browser
 Gute Unterstützung von Unit, Functional und Integration Testing
 Sehr guter JSON und JavaScript Support
 JSON Parsen, Manipulieren und Generieren
 Webjars Support und Require-JS eingebaut
 Minimization, LESS/CoffeeScript Compilation, etc.
Seite 9
© Mirko Stocker 2014
Play macht dem Entwickler Spass
Seite 10
© Mirko Stocker 2014
Play Komponenten
Integrierter Webserver (Netty)
Webservices
API
HTTP Interface
Template
Engine
Asset
Compilation
Async I/O
HTML Form
Validation
JSON
Akka Actors
Diverse (NO-) SQL Anbindungen
Seite 11
© Mirko Stocker 2014
Interaktives
Buildsystem
und
Projekttemplates
Play User
Seite 12
© Olaf Zimmermann, Mirko Stocker 2014
Neues Play Projekt Erstellen
 Play wird von Typesafe (Scala, Akka) entwickelt
 Neue Play Projekte erstellt man am einfachsten mit dem Activator
 Activator: Build-Tool und Projekt-Generator von Typesafe
 Enthält diverse Beispielprojekte und Templates
Seite 13
© Mirko Stocker 2014
Neues Play Projekt Erstellen
 Verfügbare Templates auflisten
 ./activator
list-templates
 Neues Projekt aus Template play-java erstellen
 ./activator
new
my-play-java
play-java
 Browser-basiertes UI starten
 ./activator ui
 Interaktives CLI starten
 ./activator
Im Projektordner
 Eclipse Projekt erstellen
 ./activator eclipse
 IntelliJ IDEA Projekt erstellen
 ./activator idea
Seite 14
© Mirko Stocker 2014
Projektlayout
Sourcecode
Buildfile
Static Assets
Tests
├──
├──
│
│
│
│
│
├──
├──
│
│
├──
│
├──
│
│
│
│
│
│
└──
activator
app
├── controllers
│
└── Application.java
└── views
├── index.scala.html
└── main.scala.html
build.sbt
conf
├── application.conf
└── routes
logs
└── application.log
public
├── images
│
└── favicon.png
├── javascripts
│
└── hello.js
└── stylesheets
└── main.css
test
├── ApplicationTest.java
└── IntegrationTest.java
Seite 15
© Mirko Stocker 2014
MVC in Play
 MVC Pattern lässt sich aus der Projektstruktur erahnen:
├──
│
│
│
│
│
Model
app
├── controllers
│
└── Application.java
└── views
├── index.scala.html
└── main.scala.html
Controller
View
 Models sind typischerweise im app/models Ordner zu finden
Seite 16
© Mirko Stocker 2014
URLs und Routing in Play
 Sprechende URLs sehen nicht nur schöner aus
 sie sind auch stabiler
 wir können sie Bookmarken, Twittern
 und als Entwickler auch direkt manipulieren
 URLs in Play sind first class Citizens (und nicht bloss Strings)
 Compiler prüft Verwendung
 Interne tote Links sind nicht möglich
 Alle URLs werden in conf/routes zentral deklariert
Klasse.Methode
GET
GET
GET
POST
/
/computers
/computers/new
/computers
controllers.Application.index
controllers.Application.list
controllers.Application.create
controllers.Application.save
GET
POST
/computers/:id
/computers/:id
controllers.Application.edit(id:Long)
controllers.Application.update(id:Long)
Seite 17
© Mirko Stocker 2014
Controller: Actions
 Ein (Page) Controller behandelt die eingehenden Requests
 Controller ist eine Sammlung von Actions
package controllers;
import play.*;
import play.mvc.*;
public class Application extends Controller {
public static Result index() {
return ok("It works!");
}
}
% GET -s 'http://localhost:9000'
200 OK
It works!%
Seite 18
© Mirko Stocker 2014
Controller: Actions
 Der Play Router mappt die URLs zu den Controller Methoden
GET
/
controllers.Application.index()
GET
/assets/*file
controllers.Assets.at(path="/public", file)
 Vordefinierte Hilfsmethoden für gängige HTTP Status Codes
 Beispielsweise auch für Redirects
public static Result index() {
return redirect(routes.Application.index());
}
% GET -e 'http://localhost:9000'
303 See Other
Location: /
Seite 19
© Mirko Stocker 2014
Controller: Routing
 Verschiedenste URL Pattern möglich
GET
/computers/:id
controllers.App.edit(id: Long)
GET
/computers/search
controllers.App.search(query: String)
GET
/computers
controllers.App.list(page: Int ?= 1)
GET
/computers
controllers.App.show(page: Page)
Eigene
Klasse
 Entsprechen Methoden-Parameter und müssen nicht selber geparsed
werden (sogar mit eigenen Klassen):
public static Result edit(long id) { … }
public static Result search(String query) { … }
public static Result list(int page) { … }
public static Result show(Page page) { … }
Seite 20
© Mirko Stocker 2014
Controller: Routing prüft Parameter
% GET -e 'http://localhost:9000/computers/blabla'
400 Bad Request
…
Seite 21
© Mirko Stocker 2014
JSON im Request Body
@BodyParser.Of(BodyParser.Json.class)
public static Result sayHello() {
JsonNode json = request().body().asJson();
String name = json.findPath("name").textValue();
if(name == null) {
return badRequest("Missing parameter [name]");
} else {
return ok("Hello " + name);
}
}
Jackson
% curl
--header "Content-type: application/json" --request POST
--data '{"name": "HSR"}' http://localhost:9000/sayHello
HTTP/1.1 200 OK
Content-Type: text/plain; charset=utf-8
Content-Length: 9
Hello HSR
Seite 28
© Mirko Stocker 2014
JSON im Request Body – Scala
case class Named(name: String)
def sayHello() = Action(parse.json[Named]) { request =>
val named = request.body // named hat Typ Named
Ok(s"Hello ${named.name}")
}
% curl
--header "Content-type: application/json" --request POST
--data '{"name": "HSR"}' http://localhost:9000/sayHello
Bad Request wenn fehlt
HTTP/1.1 200 OK
Content-Type: text/plain; charset=utf-8
Content-Length: 9
Generiert JSON Parser
Hello HSR
Seite 29
© Mirko Stocker 2014
JSON als Result
 JSON erstellen
public static Result sayHello() {
ObjectNode result = Json.newObject();
result.put("message", "Hello Webnesday!");
return ok(result);
}
 ok() ist überladen und setzt automatisch den richtigen Content-Type
% curl --request GET http://localhost:9000/sayHello
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
{"message":"Hello Webnesday!"}
Seite 30
© Mirko Stocker 2014
Models
 Typischerweise POJOs in app/models
 Play ist nicht auf einen spezifischen OR-Mapper festgelegt
 JPA, JDBC, etc.
 Auf der Scala Seite auch Anorm und Slick (ähnlich wie MS LINQ)
 Beispiel mit JPA:
@Transactional
public static Result list() {
EntityManager em = play.db.jpa.JPA.em();
TypedQuery<Computer> query = em.createQuery(…)
Collection<Computers> computers = query.getResultList();
return ok(views.html.Application.index.render(user, computers));
}
Seite 31
© Mirko Stocker 2014
Non-Blocking I/O
Seite 32
© Olaf Zimmermann, Mirko Stocker 2014
Das Problem mit Threads
 Threads sind teuer in der Erstellung
 Lösung: Wiederverwenden, Threadpool verwenden
 Wie gross soll der Threadpool sein?
 Aus CPU-Sicht: Ein Thread / Core
 Zu gross


Memory-Overhead
Context-Switching
 Zu klein


Abhängigkeit von Downstream-Latenz
Requests blockieren wenn keine Threads mehr vorhanden sind
 Lösung:
 Keine Threads blockieren! Ein Thread / Core dauernd beschäftigt
 Non-Blocking I/O (NIO in Java, select / poll / epoll)
Seite 33
© Olaf Zimmermann, Mirko Stocker 2014
Latenz in einem verteilten System mit Blocking
DB
Backend
Service
Web
Server
Load
Balancer
Web
Server
S3
DB
Backend
Service
DB
Backend
Service
3rd Party
Service
Seite 34
© Olaf Zimmermann, Mirko Stocker 2014
DB Latenz
geht hoch
Latenz in einem verteilten System mit Blocking
Threads werden «gestaut»
Backend
Service
Web
Server
Load
Balancer
Web
Server
DB
S3
DB
Backend
Service
DB
Backend
Service
3rd Party
Service
Seite 35
© Olaf Zimmermann, Mirko Stocker 2014
Evented vs. Threaded Web Server
 Play basiert auf Netty, einem evented Web Server
 Traditionell sind Web Server eher Thread based: pro Request ein Thread
 Event basierend: Pro CPU ein Thread, Threads werden nicht blockiert.
 Auch asynchron oder non-blocking genannt
 Siehe auch Reactor Pattern (Pattern Oriented Software Architecture 2)
 Sobald bei der Bearbeitung des Requests auf eine downstream
Datenquelle (WS, DB) gewartet werden muss, wird der Thread freigeben
und ein Callback registriert
 Event-based skaliert besser bei vielen Requests
 Thread ist nur besetzt, wenn es etwas zu tun gibt
 Unabhängigkeit von der Downstream Latenz
Seite 36
© Mirko Stocker 2014
Non-Blocking Web Services
 Controller Action in Play kann auch ein Promise<Result> zurückgeben
 Promise: analog zu Future (Proxy Objekt für ein zukünftiges Resultat)
 Aufruf der Action beendet so schnell wie möglich
 Thread wird für nächsten Request frei
 Resultat wird ausgeliefert, wenn die Berechnung fertig ist
public static Promise<Result> longRunningAction() {
System.out.println("Action entry");
String url = "http://a.really.slow.server";
}
Promise<Result> result = WS.url(url).get().map(response -> {
System.out.println("Processing result");
return ok(response.getBody()).as("text/html");
});
Action entry
System.out.println("Action exit");
Action exit
return result;
Processing result
Seite 37
© Mirko Stocker 2014
Play Zusammenfassung
 Play ist eine leichtgewichtige moderne Alternative für Java Web
 Stateless, RESTful (Cloud-Ready)
 Embrace HTTP, nicht verstecken und wegabstrahieren
 Schneller Entwicklungszyklus, kein langwieriges Deployment
 Non-blocking und evented, läuft auf Netty
 Intern in Scala geschrieben, mit Java und Scala API
 Typsicher: Routing, URLs und Templates
 LESS, CoffeeScript, React, etc. unterstützt im Buildsystem
 JSON, Websockets, SSE Support
Seite 38
© Mirko Stocker 2014
Danke für eure Aufmerksamkeit!
Seite 39
© Olaf Zimmermann, Mirko Stocker 2014
Herunterladen