Finagle & Clojure by Alexey Kachayev for #FinagleCon 2015
About Me ‣ Alexey Kachayev, @kachayev ‣ CTO at Attendify.com • Almost-all-day-coding-kind-of-CTO ‣ Active open source contributor
Attendify Use Case Detailed Event information. A fully featured event app with essential content. Private event community and social network.
Attendify Use Case ‣ Private social networks for events (thousands) ‣ A lot of microservices both in Scala & Clojure ‣ Finagle to run services written in Scala ‣ Finagle to run services written in Clojure (???)
finagle-clojure ‣ https://github.com/finagle/finagle-clojure ‣ “A thin Clojure wrapper around Finagle” ‣ Scala interop ‣ Bridge to Thrift/ThriftMux and more ‣ lein template (quick start)
lein template $ ¡lein ¡new ¡finagle-‑clojure ¡schedule ¡ $ ¡tree ¡-‑L ¡2 ¡ . ¡ ��� ¡schedule ¡ ¡ ¡ ¡ ¡ ��� ¡README.md ¡ ¡ ¡ ¡ ¡ ��� ¡project.clj ¡ ¡ ¡ ¡ ¡ ��� ¡schedule-‑client ¡ ¡ ¡ ¡ ¡ ��� ¡schedule-‑core ¡ ¡ ¡ ¡ ¡ ��� ¡schedule-‑service
Thrift definition namespace ¡java ¡schedule.thrift ¡ struct ¡SessionRequest ¡{ ¡ ¡ ¡1: ¡string ¡id ¡ } ¡ struct ¡Session ¡{ ¡ ¡ ¡1: ¡string ¡id ¡ ¡ ¡2: ¡string ¡title ¡ ¡ ¡3: ¡string ¡speaker ¡ } ¡ service ¡Schedule ¡{ ¡ ¡ ¡ ¡ ¡Session ¡fetchSession(1: ¡SessionRequest ¡req) ¡ }
Service implementation (ns ¡schedule.service ¡ ¡ ¡(:import ¡[schedule.thrift ¡Schedule ¡Session]) ¡ ¡ ¡(:require ¡[finagle-‑clojure.futures ¡:as ¡f] ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡[finagle-‑clojure.thrift ¡:as ¡thrift]) ¡ ¡ ¡(:gen-‑class)) ¡ (defn ¡make-‑service ¡ ¡ ¡[] ¡ ¡ ¡(thrift/service ¡Schedule ¡ ¡ ¡ ¡ ¡(fetchSession ¡[req] ¡ ¡ ¡ ¡ ¡ ¡ ¡(let ¡[id ¡(.id ¡req)] ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡(f/value ¡(Session. ¡id ¡"Clojure" ¡"Alexey")))))) ¡ (defn ¡-‑main ¡ ¡ ¡[& ¡args] ¡ ¡ ¡(f/await ¡(thrift/serve ¡":9999" ¡(make-‑service))))
Client implementation (ns ¡schedule.client ¡ ¡ ¡(:import ¡[schedule.thrift ¡Schedule]) ¡ ¡ ¡(:require ¡[finagle-‑clojure.futures ¡:as ¡f] ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡[finagle-‑clojure.thrift ¡:as ¡thrift])) ¡ (defn ¡make-‑client ¡ ¡ ¡[address] ¡ ¡ ¡(thrift/client ¡address ¡Schedule)) ¡ (defn ¡session-‑title ¡[client ¡id] ¡ ¡ ¡(-‑> ¡(.fetchSession ¡client ¡(SessionRequest. ¡id)) ¡ ¡ ¡ ¡ ¡ ¡ ¡(f/map ¡[v] ¡(.title ¡v))))
Client implementation (defn ¡speaker-‑name ¡[client ¡session-‑id] ¡ ¡ ¡(-‑> ¡(.fetchSession ¡client ¡(SessionRequest. ¡session-‑id)) ¡ ¡ ¡ ¡ ¡ ¡ ¡(f/flatmap ¡[v] ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡(let ¡[speaker-‑id ¡(.speaker ¡v) ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡req ¡(SpeakerRequest. ¡speaker-‑id)] ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡(-‑> ¡(.fetchSpeaker ¡client ¡req) ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡(f/map ¡[v] ¡(.firstName ¡v))))))) ¡ (defn ¡-‑main ¡[& ¡args] ¡ ¡ ¡(let ¡[c ¡(make-‑client ¡"localhost:9999")] ¡ ¡ ¡ ¡ ¡(println ¡(f/await ¡(session-‑title ¡c ¡"42"))) ¡ ¡ ¡ ¡ ¡(println ¡(f/await ¡(speaker-‑name ¡c ¡"42")))))
finagle-clojure? ‣ Scala API is not idiomatic for Clojure • easy to translate examples from the Internet • hard to play with the rest of the code ‣ Should we write Scala code using Clojure syntax?
Everything is a Data ‣ Builder API for servers & clients ‣ Hash-maps instead chained mutators ‣ Hash-maps instead of HTTP Request/Response builders
Not A Clojure (-‑> ¡(builder-‑server/builder) ¡ ¡ ¡ ¡ ¡(builder-‑server/codec ¡http-‑codec/http) ¡ ¡ ¡ ¡ ¡(builder-‑server/bind-‑to ¡3000) ¡ ¡ ¡ ¡ ¡(builder-‑server/named ¡“test”) ¡ ¡ ¡ ¡ ¡(builder-‑server/build ¡hello-‑world)) (new-‑server ¡hello-‑world ¡{:codec ¡http-‑codec/http ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡:tls ¡nil ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡:max-‑request-‑size ¡200 ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡:max-‑response-‑size ¡400 ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡:bind-‑to ¡3000 ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡:name ¡:test})
Clojure (-‑> ¡(builder-‑server/builder) ¡ ¡ ¡ ¡ ¡(builder-‑server/codec ¡http-‑codec/http) ¡ ¡ ¡ ¡ ¡(builder-‑server/bind-‑to ¡3000) ¡ ¡ ¡ ¡ ¡(builder-‑server/named ¡“test”) ¡ ¡ ¡ ¡ ¡(builder-‑server/build ¡hello-‑world)) (new-‑server ¡hello-‑world ¡{:codec ¡http-‑codec/http ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡:tls ¡nil ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡:max-‑request-‑size ¡200 ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡:max-‑response-‑size ¡400 ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡:bind-‑to ¡3000 ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡:name ¡:test})
Scala Futures in Clojure ‣ Two branches of execution (success & errors) ‣ Exception-driven errors handling ‣ Hard to manage execution context ‣ There is a better way to work with chaining
Clojure Futures ‣ A lot of “native” primitives: • futures, promises • agents • pmap, parallel reducers ‣ Clojure Futures are not that great for doing I/O
Manifold ‣ https://github.com/ztellman/manifold ‣ Abstraction for event-based async programming ‣ Streams & deferreds ‣ Deferreds: @, zip, chain, success, catch ‣ Pluggable concurrent executors
Finagle & Deferred (def ¡req ¡(SessionRequest. ¡"42")) ¡ (def ¡f1 ¡(.fetchSession ¡client ¡req)) ¡ (def ¡d4 ¡(d/deferred)) ¡ (f/on-‑success ¡f1 ¡[v] ¡(d/success! ¡d4 ¡v)) ¡ (f/on-‑failure ¡f1 ¡[v] ¡(d/error! ¡d4 ¡v)) ¡ user> ¡@d4 ¡ user> ¡#object[Session[…]] ¡ user> ¡@(d/chain ¡d4 ¡#(.title ¡%)) ¡ user> ¡"Finagle ¡& ¡Clojure"
Finagle & Manifold (defn ¡future-‑>deferred ¡[sf] ¡ ¡ ¡(let ¡[d1 ¡(d/deferred)] ¡ ¡ ¡ ¡ ¡(f/on-‑success ¡sf ¡[v] ¡(d/success! ¡d1 ¡v)) ¡ ¡ ¡ ¡ ¡(f/on-‑failure ¡sf ¡[v] ¡(d/error! ¡d1 ¡v)) ¡ ¡ ¡ ¡ ¡d1)) ¡ (defn ¡fetch-‑session ¡[c ¡id] ¡ ¡ ¡(f-‑>d ¡(.fetchSession ¡c ¡(SessionRequest. ¡id)))) ¡ (defn ¡fetch-‑speaker ¡[c ¡id] ¡ ¡ ¡(f-‑>d ¡(.fetchSpeaker ¡c ¡(SpeakerRequest. ¡id))))
Let-Flow (defn ¡speaker-‑name ¡[c ¡session] ¡ ¡ ¡(let-‑flow ¡[session ¡(fetch-‑session ¡c ¡session) ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡sid ¡(.speaker ¡session) ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡speaker ¡(fetch-‑speaker ¡c ¡sid)] ¡ ¡ ¡ ¡ ¡(str ¡(.firstName ¡speaker) ¡" ¡" ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡(.lastName ¡speaker)))) ¡ user> ¡(speaker-‑name ¡client ¡"42") ¡ user> ¡#object[Deferred] ¡ user> ¡@(speaker-‑name ¡client ¡"42") ¡ user> ¡"Alexey ¡Kachayev"
Let-Flow (defn ¡speaker-‑name ¡[c ¡session] ¡ ¡ ¡(let-‑flow ¡[session ¡(fetch-‑session ¡c ¡session) ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡sid ¡(.speaker ¡session) ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡speaker ¡(fetch-‑speaker ¡c ¡sid)] ¡ ¡ ¡ ¡ ¡(str ¡(.firstName ¡speaker) ¡" ¡" ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡(.lastName ¡speaker)))) values deferreds
Recommend
More recommend