Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

JVM Concurrency auf der JavaLand am 8. März 2016

724 views

Published on

Neue JVM-Concurrency-Modelle wie Aktoren und Verticles

Nebenläufigkeit heißt in Java: java.util.concurrent und Threads. Threads wurden ursprünglich als "lightweight processes" tituliert. Neue Prozesse zu starten, um nebenläufige Operationen zu implementieren, bedeutete zu viel Overhead und hat das Problem der Kommunikation zwischen den Prozessen aufgeworfen. Threads sollten "leicht" sein und beide Nachteile beseitigen: weniger Ressourcen für die Erzeugung und Scheduling, und gemeinsame Speichernutzung.

Allerdings stößt das Modell heute an seine Grenzen. Der Ressourcenverbrauch ist immer noch zu groß und der gemeinsame Speicher mehr Problem als Lösung: Race Conditions, Locks, Contention. Um Oracles Java-VM-Architekten John Rose zu zitieren: "Threads sind passé". Wir wollen verschiedene Ansätze betrachten, Nebenläufigkeit unterhalb des Thread-Levels umzusetzen, und ihre Vor- und Nachteile beleuchten. Vorgestellt werden Quasar Fibers, Clojure Agents, Vert.x Verticles und Akka Actors.

Published in: Software
  • Be the first to comment

  • Be the first to like this

JVM Concurrency auf der JavaLand am 8. März 2016

  1. 1. Javaland 2016 Concurrency-Modelle auf der JVM Lutz Hühnken @lutzhuehnken https://github.com/lutzh/concurrency_talk
  2. 2. Nebenläufigkeit in der Literatur
  3. 3. Provokante These (bitte später beim Bier diskutieren): Java braucht eine neue Concurrency - Standardbibliothek!
  4. 4. Warum reichen Threads nicht aus? Problem 1: Effizienz (bzw. Mangel an derselben)
  5. 5. CPU Threads Arbeit
  6. 6. 1 2 3 … 10.000 4-core CPU Tausende Threads! Jede Menge Arbeit!
  7. 7. Das ist, wo wir hinwollen:
  8. 8. Von ThreadProzess Prozess „Worker“ „Task“ Zu Siehe auch https://en.wikipedia.org/wiki/Reactor_pattern
  9. 9. Wichtiger Aspekt: (blocking) I/O Wenn jeder Thread nur jeweils einen Task hat: (Dies ist eine Momentaufnahme, kein zeitlicher Ablauf)
  10. 10. Niemals blockieren! Wenn ein Thread ein „Worker“ ist, der viele Tasks bearbeitet: (Dies ist eine Momentaufnahme, kein zeitlicher Ablauf)
  11. 11. Effizienz-Problem: Gelöst! • Nebenläufigkeit unterhalb des Thread-Levels (Tasks) • Asynchrone I/O
 • Dies haben alle Modelle, die wir uns ansehen werden, gemeinsam!
 • Das haben übrigens auch alle reaktiven* Frameworks gemeinsam!
 * http://www.reactivemanifesto.org
  12. 12. Warum reichen Threads nicht aus? Problem 1: Effizienz (bzw. Mangel an derselben) Problem 2: Programmiermodell
  13. 13. They discard the most essential and appealing properties of sequential computation: understandability, predictability, and determinism. Threads, as a model of computation, are wildly nondeterministic, and the job of the programmer becomes one of pruning that nondeterminism.
  14. 14. Quelle: John Rose, Java VM Architect, JFokus, Stockholm, February 2015
  15. 15. Exkurs: Die Callback-Hölle fs.readdir(source, function(err, files) { if (err) { console.log('Error finding files: ' + err) } else { files.forEach(function(filename, fileIndex) { console.log(filename) gm(source + filename).size(function(err, values) { if (err) { console.log('Error identifying file size: ' + err) } else { console.log(filename + ' : ' + values) aspect = (values.width / values.height) widths.forEach(function(width, widthIndex) { height = Math.round(width / aspect) console.log('resizing ' + filename + 'to ' + height + 'x' + height) this.resize(width, height).write(destination + 'w' + width + '_' + filename, function(err) { if (err) console.log('Error writing file: ' + err) }) }.bind(this)) } }) }) } })
  16. 16. Zugegeben: Das könnte man schöner schreiben • Scala Futures / for-expressions • async / await (auch Scala) • JavaScript Promises (then.. then.. then)
 • All das ist „syntactic sugar“ für Callbacks - was eine gute Sache ist! • Aber lasst uns nach anderen Ansätzen für asynchrone Komposition schauen.
  17. 17. Also was interessiert uns? Komposition Zustand Integration, I/O (async und sync) Vorhersagbarkeit
  18. 18. Coroutines (Green Threads, User Mode Threads, IOC Threads, Fibers) Quasar Fibers http://www.paralleluniverse.co/quasar/ Channels Clojure core.async https://github.com/clojure/core.async/ Event Loop vert.x http://vertx.io Actor Model Akka http://akka.io Concurrency - Modelle
  19. 19. Fibers
  20. 20. new Fiber<V>() { @Override protected V run() throws SuspendExecution, InterruptedException { // code here } }.start(); sieht aus wie ein Thread, redet wie ein Thread
  21. 21. class RetrieveInfo extends Fiber<Void> { ... @Override public Void run() { final CloseableHttpClient client = FiberHttpClientBuilder.create().build(); try { String response = client.execute(new HttpGet(MessageFormat.format(url, searchTerm)), new BasicResponseHandler()); addResult(key, StringUtils.abbreviate(response, 1000)); } catch (IOException iox) { ... Use like blocking Need to aggregate results.. „fiber blocking“ async http client
  22. 22. private final Map<String, String> store = new ConcurrentHashMap<>(); public void define(final String searchTerm) { for (String currentKey : sources.keySet()) { new RetrieveInfo(currentKey, sources.get(currentKey), searchTerm).start(); } } /** * This method is working on shared mutual state - this is what we look to avoid! */ void addResult(final String key, final String result) { store.put(key, result); ...Methode, um Ergebnis entgegenzunehmen - muss „locken" - wollen wir nicht!! shared mutable state Starte Web-Aufrufe parallel im Hintergrund
  23. 23. Fibers • Größter Vorteil: Imperative Programmierung, wie wir es von Threads kennen.
 • Größter Nachteil: Imperative Programmierung, wie wir es von Threads kennen!
 

  24. 24. class FooAsync extends FiberAsync<String, FooException> implements FooCompletion { @Override public void success(String result) { asyncCompleted(result); } @Override public void failure(FooException exception) { asyncFailed(exception); } } String op() { new FooAsync() { protected void requestAsync() { Foo.asyncOp(this); } }.run(); } Verwandelt callback APIs in fiber blocking APIs!
  25. 25. Fibers Zusammenfassung • Thread-artiges Programmiermodell • Nette „Tricks“: Instrumentation, Suspendable, Thread Interop • Fibers alleine sind ziemlich „low level“ • (Fast) 1:1 Ersatz für Threads
  26. 26. Channels
  27. 27. Channels • Theoretische Basis:
 Communicating Sequential Processes (Tony Hoare 1978, https:// en.wikipedia.org/wiki/ Communicating_sequential_processes) • Elementarer Bestandteil von „Go“ • Unser Beispiel: Clojure core.async Channels
  28. 28. (def echo-chan (chan)) (go (println (<! echo-chan))) (>!! echo-chan „ketchup") ; => true ; => ketchup go block (wie ein Fiber) >!! blocks thread <! blocks go block
  29. 29. (def echo-buffer (chan 2)) (>!! echo-buffer "ketchup") ; => true (>!! echo-buffer "ketchup") ; => true (>!! echo-buffer "ketchup") ; blocks buffered channel - async
  30. 30. (def responses-channel (chan 10)) (go (println (loop [values []] (if (= 3 (count values)) values (recur (conj values (<! responses-channel))))))) (defn async-get [url result] (http/get url #(go (>! result (:body %))))) (async-get "http:m-w.com..." responses-channel) (async-get "http:wiktionary.." responses-channel) (async-get "http:urbandictionary.." responses-channel) callback puts result in channel channel for responses Aggregate results recursively
  31. 31. Channels • Erlaubt Datenfluss-orientierte Programmierung, flexible Komposition (z.B. alts!) • Reduktion: Immutability, sequentielles Konsumieren • go block / thread Dualität erlaubt Integration mit async und sync Libraries • Channels als Hüter des Zustandes? Channels Zusammenfassung
  32. 32. Event Loop
  33. 33. Channels • Event loop model - Vorbild node.js • „multi event loop“, auf der JVM • Polyglott - Java, Scala, Clojure, JRuby, JS.. • Keine Einheiten (Verticles) deployen, die per JSON kommunizieren vert.x
  34. 34. public class Receiver extends AbstractVerticle { @Override public void start() throws Exception { EventBus eb = vertx.eventBus(); eb.consumer("ping-address", message -> { System.out.println("Received message: " + message.body()); // Now send back reply message.reply("pong!"); }); System.out.println("Receiver ready!"); } } register on event bus extendVerticle connect
  35. 35. MessageConsumer<JsonObject> consumer = eventBus.consumer("definer.task"); consumer.handler(message -> { httpClient.getNow(message.body().getString("host"), message.body().getString("path"), httpClientResponse -> { httpClientResponse.bodyHandler(buffer -> { eventBus.send("collector", new JsonObject() .put(...) callback based async http register send result as message
  36. 36. Vertx vertx = Vertx.vertx(); EventBus eventBus = vertx.eventBus(); DeploymentOptions options = new DeploymentOptions().setInstances(sources.size()); vertx.deployVerticle("de.huehnken.concurrency.Task", options, msg -> { for (String currentKey : sources.keySet()) { eventBus.send("definer.task", new JsonObject() .put(..) which one gets the message? deploy multiple
  37. 37. private final Map<String, String> store = new HashMap<>(); @Override public void start(Future<Void> startFuture) { EventBus eventBus = vertx.eventBus(); MessageConsumer<JsonObject> consumer = eventBus.consumer("collector"); consumer.handler(message -> { store.put(message.body().getString(„key"), message.body().getString("definition")); wait for results.. encapsultate state inVerticle messages are usually JSON
  38. 38. Event Bus (vert.x) Image from Jonas Bandi @jbandi
  39. 39. Channels • (Sehr) lose Kopplung • Reduktion: Events und „Single Thread Illusion“ • Hybrides Thread-Modell erlaubt integration synchroner APIs, und dem Event-Loop Arbeit woanders abzuladen. • Zustands erfordert etwas Aufmerksamkeit • APIs sind sehr Callback-lastig, node.js inspiriert • Verteilt! Einheitliches Modell für lokale und verteilte Nebenläufigkeit! vert.x Summary
  40. 40. Actors
  41. 41. Channels • The actor model [..] is a [model] that treats "actors" as the universal primitives of concurrent computation: in response to a message that it receives, an actor can make local decisions, create more actors, send more messages, and determine how to respond to the next message received. https://en.wikipedia.org/wiki/Actor_model • Entwickelt von Carl Hewitt 1973 • Verbreitung durch Erlang Akka
  42. 42. Actors (Akka)
  43. 43. Actors (Akka)
  44. 44. class Coordinator(searchTerm: String) extends Actor { var results = Map[String, String]() def receive = { case StartCommand => sources foreach { case (key, url) => context.actorOf(Task.props(key, self)) ! GetDefinition(url) } context.system.scheduler.scheduleOnce(5 seconds, self, Done) case Result(key, definition) => results += key -> definition if (results.size == sources.size) { self ! Done } case Done => erzeuge Child Actors, sende Nachricht Zustand, gekapselt aggregiere Ergebnisse
  45. 45. class Task(key: String, coordinator: ActorRef) extends Actor { def receive = { case GetDefinition(url) => http.singleRequest(HttpRequest(uri = url)) pipeTo self case HttpResponse(StatusCodes.OK, headers, entity, _) => coordinator ! Result(key, entity.dataBytes.utf8String) case HttpResponse(code, _, _, _) => // handle error send result to aggregator async call, translated to message
  46. 46. Actors (Akka)
  47. 47. Channels • Reduktion: Nachrichten und „Single Thread Illusion“ • Messages (as opposed to events) • „Dispatcher“ erlauben Integration synchroner APIs und das Abladen von Arbeit • pipeTo & ask zur Integration von asynchronen APIs • Verteilt! Einheitliches Modell für lokale und verteilte Nebenläufigkeit! • Supervision! Fehlerbehandlung eingebaut! Actors Summary
  48. 48. Concurrency-Modelle Zusammenfassung Tasks (sub- thread level) Asynchronous Messaging Distribution (unified model) Supervision Fibers ✔ Channels (core.async) ✔ ✔ Event Bus (vert.x) ✔ ✔ ✔ Aktoren (Akka) ✔ ✔ ✔ ✔
  49. 49. • Im Bereich Concurrency ist einiges los! • Threads are passé! • Sie sind die Basis auf OS- und JVM-Ebene, aber Entwickler benötigen ein anderes, besseres Modell. • Alternativen sind vorhanden, auf der JVM, hier und heute. • Wenn eure Zeit knapp ist, und ihr euch nur ein anderes Modell ansehen könnt, empfehle ich Akka. Zusammenfassung
  50. 50. Der Kreis schließt sich - noch ein Buch
  51. 51. Vielen Dank Lutz Hühnken @lutzhuehnken https://github.com/lutzh/concurrency_talk
  52. 52. Aber ich will doch nur einen kleinen REST-Service bauen.. • Wenn du nicht auf dieser Ebene programmierst - kenne deine Frameworks! • Halte Ausschau nach „reactive“ oder „async“ • Play! • Akka HTTP • Red Hat vert.x • (Pivotal Reactor? Rat pack?)
  53. 53. Achtung bei Benchmarks! • Ungeeignet: CPU-lastige Benchmarks • Ungeeignet: Alles, was synchrone I/O verwendet • Es geht nicht um die Geschwindigkeit bei einzelnen Requests, sondern um Skalierbarkeit (Little’s Law, L=𝛌W)
  54. 54. Agents
  55. 55. (def x (agent 0)) (defn increment [c n] (+ c n)) (send x increment 5) ; @x -> 5 (send x increment 10) ; @x -> 15 „wrap“ a value in an agent send the code to the agent to modify value! a function
  56. 56. Agenten • Agenten kapseln Zustand • Sende ausführbaren Code an Agenten, um Zustand zu ändern • Interessantes Feature, besonders in der Funktionalen Programmierung • Der Code wird asynchron ausgeführt, aber allein ist es nicht wirklich ein Concurrency-Modell
  57. 57. Der Vollständigkeit halber • Quasar bietet auch Channels, und sogar Actors • Akka bietet auch Busse (lokal und verteilt), und Agenten. • Vieles stammt von außerhalb der JVM-Welt (Go Channels, node.js, Erlang Actors)
  58. 58. Jetzt ist wirklich Schluss! Lutz Hühnken @lutzhuehnken https://github.com/lutzh/concurrency_talk
  59. 59. Image Credits Cat on hot tin roof - from "McPhillamyActorBlog" - http://mcphillamy.com Agents - from "Matrix Wiki" - http://matrix.wikia.com/wiki/Agent Event Loop - http://www.freeimages.com/photo/big-looping-1522456 Channel - http://www.freeimages.com/photo/white-cliffs-of-dover-1256973 Fibers - http://www.freeimages.com/photo/fiber-optics-1503081

×