Lars Röwekamp | CIO New Technologies | open knowledge GmbH
#WISSENTEILEN@_openKnowledge
@mobileLarson
Web APIs jenseits von REST & Request/Response
ÜBER OPEN KNOWLEDGEBranchenneutrale Softwareentwicklung und IT-Beratung
#WISSENTEILEN
ÜBER MICH
Wer bin ich - und wen ja, wie viele?
• CIO New Technologies • Enterprise & Mobile • Autor, Speaker, Coach & Mentor
• Snowboard & MTB Enthusiast• Mehrfacher Vater, einfacher Ehemann
Lars Röwekamp (a.k.a. @mobileLarson)
#WISSENTEILEN
LR
Living in a RESTful World
#WISSENTEILEN
lang laufende Anfragenserverseitige Ereignissekomplexe Abfragen
„Sorry, ich bin leider nicht so gut in ...“*
*REST @work
„But it‘s notREST if youdo (not) ...“
Mein Server und ich ...
#WISSENTEILEN
„WT#!?“ *
(*a.k.a. „i don‘t care as long as it works for me.“)
#WISSENTEILEN
lang laufendeAnfragen
#WISSENTEILEN
Request-Response Modell
#WISSENTEILEN
Request-Response Modell
#WISSENTEILEN
Request-Response Modell
#WISSENTEILEN
I had a Dream
#WISSENTEILEN
Reactive-Flow Modell
#WISSENTEILEN
public interface ReactiveMongoDBOperations {
<T> Mono<T> find (Query query, Class<T> entityClass);
<T> Flux<T> findAll(Query query, Class<T> entityClass);
<T> Mono<T> insert(T objectToSave, String collectionName);
<T> Mono<T> insert(Mono<? extends T> objectToSave);
}
#WISSENTEILEN
RouterFunction<?> route = route(GET("/persons/{id}"), request -> {Mono<Person> person = Mono.justOrEmpty(request.pathVariable("id")).map(Integer::valueOf).then(repository::find);
return Response.ok().body(fromPublisher(person, Person.class));}));
#WISSENTEILEN
RouterFunction<?> route = route(GET("/persons/{id}"), request -> {Mono<Person> person = Mono.justOrEmpty(request.pathVariable("id")).map(Integer::valueOf).then(repository::find);
return Response.ok().body(fromPublisher(person, Person.class));}).and(route(GET("/persons"), request -> {Flux<Person> people = repository.findAll();return Response.ok().body(fromPublisher(people, Person.class));
}));
#WISSENTEILEN
RouterFunction<?> route = route(GET("/persons/{id}"), request -> {Mono<Person> person = Mono.justOrEmpty(request.pathVariable("id")).map(Integer::valueOf).then(repository::find);
return Response.ok().body(fromPublisher(person, Person.class));}).and(route(GET("/persons"), request -> {Flux<Person> people = repository.findAll();return Response.ok().body(fromPublisher(people, Person.class));
}).and(route(POST("/persons"), request -> {Mono<Person> person = request.body(toMono(Person.class));return Response.ok().build(repository.insert(person));
}));
#WISSENTEILEN
„Are u kidding?“?
#WISSENTEILEN
HTTP /1.0 202 AcceptedLocation: http://....
#WISSENTEILEN
Fire & Forget*
#WISSENTEILEN
Polling Modell
#WISSENTEILEN
@PUT@Consumes(MediaType.APPLICATION_JSON)public Response processOrder(Order order) throws ... {
UUID orderId = orderService.process(order);return Response
.accepted(). // HTTP accepted 202
.location(new URI(„orders/“ + orderId + „/status“))
.build();}
Fire & Forget* mit JAX-RS
(* eigentlich Polling!)
#WISSENTEILEN
@GET @Path("/{orderNo}") @Produces(MediaType.APPLICATION_JSON) public Response getOrder(@PathParam("orderNo") UUID orderNo) {
Order order = orderService.getOrder(orderNo);if (order.isEmpty()) {return Response.noContent().build();
} else {return Response.ok().entity(order).build();
} }
Polling mit JAX-RS
#WISSENTEILEN
Long-Polling
#WISSENTEILEN
Long Polling Modell
#WISSENTEILEN
@POSTpublic void createCustomer(Customer customer,
@Suspended AsyncResponse asyncResponse) {
Response response = createCustomer(customer);asyncResponse.resume(response);
}
asynchronous JAX-RS 2.0
#WISSENTEILEN
@POSTpublic void createCustomer(Customer customer,
@Suspended AsyncResponse asyncResponse) {
asyncResponse.setTimeout(10, TimeUnit.SECONDS);asyncResponse.setTimeoutHandler(ar -> ar.resume(
Response.status(Status.SERVICE_UNAVAILABLE).entity("Operation timed out").build()));
Response response = createCustomer(customer);asyncResponse.resume(response);
}
asynchronous JAX-RS 2.0
#WISSENTEILEN
@POSTpublic void createCustomer(final Customer customer,
@Suspended final AsyncResponse asyncResponse) {
asyncResponse.setTimeout(10, TimeUnit.SECONDS);asyncResponse.setTimeoutHandler(...);
executor.execute(() -> {Response response = createCustomer(customer);asyncResponse.resume(response);
});}
asynchronous JAX-RS 2.0
#WISSENTEILEN
@POSTpublic void createCustomer(final Customer customer,
@Suspended final AsyncResponse asyncResponse) {
asyncResponse.register(throwable -> {if (throwable != null) {LOG.error("Operation failed.", t);return;
}LOG.info("Operation successful.");
});...
}
asynchronous JAX-RS 2.0
#WISSENTEILEN
Client client = ClientBuilder.newBuilder().build().target("https://...").request().async().get(new InvocationCallback<Result>() {public void completed(Result result) {...
}public void failed(Throwable error) {...
}});
asynchronous JAX-RS 2.0
#WISSENTEILEN
Piggy-Back
#WISSENTEILEN
Piggy Bag Modell
#WISSENTEILEN
HTTP/1.0 200 OKServer: ...MIME-Version: 1.0Content-Type: multipart/x-mixed-replace;boundary=EOM
--EOMContent-Type application/json{...}--EOMContent-Type application/octet-stream{...}--EOM
Piggy Back mit REST
#WISSENTEILEN
Piggy Back mit JAX-RS 2.0
(*nur via eigenem MessageBodyWriter)
#WISSENTEILEN
public class BooksResource {
@Produces("multipart/mixed")public Map<String, Object> getBooks() {
Map<String, Object> map= new LinkedHashMap<String, Object>();
map.put("text/xml", new JaxbBook());map.put("application/json", new JSONBook());map.put("application/octet-stream", imageInputStream);return map;
}...
}
Piggy Back mit APACHE CXF
#WISSENTEILEN
serverseitigeEreignisse
#WISSENTEILEN
serverseitigeEreignisse
#WISSENTEILEN
#WISSENTEILEN
#WISSENTEILEN
#WISSENTEILEN
Was steckt dahinter?
• Paradigmenwechsel in der Kommunikation• Abkehr vom klassischen Request/Response
• Client meldet sich am Server an• Server kann Client jederzeit “Daten“ schicken• Session kann von beiden Seiten beendet werden
Serverseitige Ereignisse
#WISSENTEILEN
Server-Sent Events
#WISSENTEILEN
Server-Sent Event Modell
#WISSENTEILEN
Was sind Server-Sent Events?
• senden von Daten vom Server zum Client • nutzt normales HTTP Protokoll• built-in Support für Reconnection• via JS, falls kein Browser Support vorhanden
• lediglich UTF-8 Daten (keine Binärdaten)• Limitierung der offenen Connections
Server-Sent Event 1x1
#WISSENTEILEN
HTTP/1.0 202 AcceptedContent-Type: text/event-stream
event: event-nameid: unique iddata: custom data
Server-Sent Event Protokoll
#WISSENTEILEN
var eventSource = new EventSource(url);eventSource.addEventListener("event-name", function(e) {
...
});
Server-Sent Event JS Client
#WISSENTEILEN
@WebServlet(urlPatterns = "/status", asyncSupported = true)public class SseServlet extends HttpServlet {
...protected void doGet(HttpServletRequest req, HttpServletResponse res)
throws IOException {AsyncContext context = req.startAsync();... // put context to queue and finish doGet (connect)
}
protected void asyncProcessing() {context.getResponse().getWriter();
}}
Server-Sent Event mit AsyncServlet*
(*SSE „hand-made“ Edition)
#WISSENTEILEN
// register process...private SseBroadcaster channel;
@GET@Path("subscribe")@Produces(MediaType.SERVER_SENT_EVENTS) // "text/event-stream"public void subscribe(@Context Sse sse, @Context SseEventSink eventSink) {String uuid = UUID.randomUUID().toString();OutboundSseEvent event = sse.newEventBuilder().id(uuid).data(“Subscription accepted. ID – “+ uuid).build();
eventSink.send(event).channel.register(eventSink);
}
Server-Sent Event mit Java EE 8*
(*SSE „Standard“ Edition)
#WISSENTEILEN
// broadcast to registerd client...private SseBroadcaster channel;
public void update() {
OutboundSseEvent event = sse.newEventBuilder().name(...).data(myJsonObject).mediaType(APPLICATION_JSON_TYPE).build();
channel.broadcast(event);
}
Server-Sent Event mit Java EE 8*
(*SSE „Standard“ Edition)
#WISSENTEILEN
WebSockets
#WISSENTEILEN
Websocket Modell
#WISSENTEILEN
Was ist WebSockets?
• Full-Duplex Real-Time Kommunikation• extrem leichtgewichtig dank HTTP Upgrade • breiter Browser-Support
• Client eröffnet die Session• Client und/oder Server sendet Nachricht(en)• Client und/oder Server beendet Session
WebSockets 1x1
#WISSENTEILEN
#WISSENTEILEN
var socket = new WebSocket("ws://host:8080/path/");
socket.onopen = function(event) {...}socket.onmessage = function(event) {...}socket.onclose = function(event) {...}socket.onerror = function(event) {...}socket.send("Message-Inhalt");
WebSocket JS Client
#WISSENTEILEN
@ServerEndpoint("/socket")public class MySocketEndpoint {
@OnOpenpublic void open(Session newSession) {try {newSession.getBasicRemote().sendText("hello, thx for connecting");
} catch (IOException | EncodeException e) {...}}
@OnMessagepublic void receive(String message) { ... }
}
WebSocket mit Java EE 7+
#WISSENTEILEN
komplexeAbfragen
#WISSENTEILEN
// Bestellungen: Kaffee mit Milch zu 2 €
GET /orders?type=coffee&ingredient=milk&price=2
Komplexe Abfragen mit REST
btw: UND oder ODER oder UND/ODER?
#WISSENTEILEN
// Bestellungen: Kaffee mit Milch oder günstiger als 2 €
GET /orders?type=coffee;ingredient=milk,price<=2
Komplexe Abfragen mit REST
btw: Klammerung?
#WISSENTEILEN
// Bestellungen: Kaffee mit Milch oder günstiger als 2 €
// RQL query ... (must possibly be encoded!)GET /orders?query=
and(eq(type,coffee),or(eq(ingredients,MILK),lt(price,2))
// RQL alternative query – FIQL (URI friendly) - ... GET /orders?query=
type==coffee;(ingredients==MILK,price=lt=2)
Komplexe Abfragen mit RQL*
*https://github.com/persvr/rql
#WISSENTEILEN
Resource Query Language
• Object-Style Query Language• FIQL Superset (erweiterbar)
• Spezifikation & JS Parser (Client & Server)• JS Array, SQL, MongoDB, Elastic Search• Java Parser + JPA Criteria Builder
Komplexe Abfragen mit RQL*
*https://github.com/persvr/rql
#WISSENTEILEN
Komplexe Abfragen mit REST
#WISSENTEILEN
Wer genau ist eigentlich dieser „Luke“?
GET /people/1
Komplexe Abfragen mit REST
#WISSENTEILEN
Wer genau ist eigentlich dieser „Luke“ und in welchen Filmen hat er mitgespielt?
GET /people/1/films (falls es der Endpoint anbietet)
GET /films/1GET /films/2GET /films/3
Komplexe Abfragen mit REST
#WISSENTEILEN
Wer genau ist eigentlich dieser „Luke“ und in welchen Filmen hat er mitgespielt und auf welchen Raumschiffen ist er darin gefahren?
GET /people/1/starships (falls es der Endpoint anbietet)
GET /starships/12GET /starships/22
Komplexe Abfragen mit REST
#WISSENTEILEN
Wie alt ist Luke und welche Haarfarbe hat er? Mit welchem Spruch wurden die Filme eröffnet, in denen er mitgespielt hat? Und wie teuer waren überhaupt die Raumschiffe auf denen er gefahren ist?
GET /?F#CKING/detail (n+1 problem)GET /?F#CKING/all (job problem)
Komplexe Abfragen mit REST
#WISSENTEILEN
Umdenken, du musst!
#WISSENTEILEN
Motivation:
„ We see a conflict between the desire to load all information in a single round trip while keeping REST resources well isolated.”
Komplexe Abfragen: „Umdenken du musst!“
(Facebook, 2012)
#WISSENTEILEN
Motivation:
Komplexe Abfragen: „Umdenken du musst!“
(Facebook, 2012)
#WISSENTEILEN
GraphQL Idee:
• Beliebige Abfragen via Objekt-Graph auf dem ich navigieren kann.
• Lieferung des Abfrage-Results in einem einzigen Round-Trip.
Komplexe Abfragen: „Umdenken du musst!“
#WISSENTEILEN
#WISSENTEILEN
#WISSENTEILEN
#WISSENTEILEN
Was zeichnet GraphQL aus?
• hierarchicial• product centric• strongly typed & instrospective• client specific queries• application-layer protokol
GraphQL Charakteristika
#WISSENTEILEN
Fragen, was du willst,du musst!
#WISSENTEILEN
#WISSENTEILEN
Komplexe Abfragen mit GraphQLWie alt ist Luke und welche Haarfarbe hat er?
Mit welchem “Spruch“ wurden die Filme eröffnet in denen er mitgespielt hat?
Und wie teuer waren überhaupt die Schiffe auf denen er gefahren ist?
#WISSENTEILEN
{person(name:„Luke“) {
haircolor, age
films {name,openiningCrawl
}
starships {name, price
}}
}
Komplexe Abfragen mit GraphQLWie alt ist Luke und welche Haarfarbe hat er?
Mit welchem “Spruch“ wurden die Filme eröffnet in denen er mitgespielt hat?
Und wie teuer waren überhaupt die Schiffe auf denen er gefahren ist?
#WISSENTEILEN
{person(name:„Luke“) {
haircolor, age
films {name,openiningCrawl
}
starships {name, price
}}
}
Komplexe Abfragen mit GraphQLWie alt ist Luke und welche Haarfarbe hat er?
Mit welchem “Spruch“ wurden die Filme eröffnet in denen er mitgespielt hat?
Und wie teuer waren überhaupt die Schiffe auf denen er gefahren ist?
#WISSENTEILEN
{person(name:„Luke“) {
haircolor, age
films {name,openiningCrawl
}
starships {name, price
}}
}
Komplexe Abfragen mit GraphQLWie alt ist Luke und welche Haarfarbe hat er?
Mit welchem “Spruch“ wurden die Filme eröffnet in denen er mitgespielt hat?
Und wie teuer waren überhaupt die Schiffe auf denen er gefahren ist?
#WISSENTEILEN
{person(name:„Luke“) {
haircolor, age
films {name,openiningCrawl
}
starships {name, price
}}
}
Komplexe Abfragen mit GraphQLWie alt ist Luke und welche Haarfarbe hat er?
Mit welchem “Spruch“ wurden die Filme eröffnet in denen er mitgespielt hat?
Und wie teuer waren überhaupt die Schiffe auf denen er gefahren ist?
#WISSENTEILEN
{person(name:„Luke“) {
haircolor, age
films {name,openiningCrawl
}
starships {name, price
}}
}
Komplexe Abfragen mit GraphQLWie alt ist Luke und welche Haarfarbe hat er?
Mit welchem “Spruch“ wurden die Filme eröffnet in denen er mitgespielt hat?
Und wie teuer waren überhaupt die Schiffe auf denen er gefahren ist?
#WISSENTEILEN
{person(name:„Luke“) {
haircolor, age
films {name,openiningCrawl
}
starships {name, price
}}
}
Komplexe Abfragen mit GraphQLWie alt ist Luke und welche Haarfarbe hat er?
Mit welchem “Spruch“ wurden die Filme eröffnet in denen er mitgespielt hat?
Und wie teuer waren überhaupt die Schiffe auf denen er gefahren ist?
#WISSENTEILEN
#WISSENTEILEN
#WISSENTEILEN
Woher weiß das Backend was ich will?
• API Schema als gemeinsame Basis• Query als Anfrage vom Client
• Step 1: parse (Query into an abstract syntax tree)*• Step 2: validate (against Schema)**• Step 3: execute (Resolve Functions)
GraphQL und das Backend
(*syntaktisch korrekt , **semantisch korrekt)
#WISSENTEILEN
// A very simple GraphQL schema example
type Character {name: String! // non nullableappearsIn: [Episode]! // array of ..., non nullable
}
type Starship {id: ID! // unique ID name: String!length(unit: LengthUnit = METER): Float // parameter
}
GraphQL und das Backend
#WISSENTEILEN
GraphQLvs.REST Aggregation?
Aggregation?
Aggregation?
#WISSENTEILEN
GraphQLvs.REST
Aggregation!
#WISSENTEILEN
Der GraphQL Server
• Query Parser• Data Fetcher• Data Aggregator
• Chaching von Result Graphs• Aktuallisierung von Result Graphs
GraphQL und das Backend
#WISSENTEILEN
Muss ich das alles selber bauen?
• Nein! Es gibt ... • Clients, Server, DB, Libraries, Tools ...
für Java, JavaScript, Python, Ruby, PHP, C/C++, GO, Scala, .Net, SQL, ...
> http://graphql.org> https://github.com/chentsulin/awesome-graphql
GraphQL und das Backend
#WISSENTEILEN
Cool, ist das wirklich alles so einfach?
• relative neue Technologie (im Fluss)
• Chaching?• Dupletten?• Information Hiding?• Versionierung? (vs. deprecated)
GraphQL und Ich
#WISSENTEILEN
? ? ?FRAGEN
#WISSENTEILEN
Kontakt
LARS RÖWEKAMPCIO NEW TECHNOLOGIES
[email protected]+49 (0)441 4082 – 101
@mobileLarson@_openknowledge
OFFENKUNDIGGUT
#WISSENTEILEN
Bildnachweise
#01: © kenishirotie – shutterstock.com#13: © RichVintage – istockphoto.com#18: © pathdoc – shutterstock.com#67: © jpgfactory – istockphoto.com
All other pictures inside this presentation orginate frompixabay.com.
#WISSENTEILEN
Top Related