Share
Embed
Transcript
35 slides
-
FREESTYLE FREESTYLE A COHESIVE & PRAGMATIC FRAMEWORK OF FP CENTRIC
SCALA LIBRARIES 1 -
Getting into typed FP is hard because Getting into typed
FP is hard because No previous CT knowledge or math foundations Leaving styles one is used to (i.e. OOP) Rapid changing ecosystem 2 -
Freestyle's Goals Freestyle's Goals Approachable to newcomers Stack-safe Dead simple
integrations with Scala's library ecosystem Help you build pure FP apps, libs & micro- services 3 -
In this talk In this talk Freestyle programming style RPC
based Microservices gRPC Freestyle RPC 4 -
Freestyle's Workflow Freestyle's Workflow Declare your algebras Compose your programs
Provide implicit implementations of each algebra Run your programs at the edge of the world 5 -
Freestyle's Workflow Freestyle's Workflow Declare your algebras @tagless(true) trait ArithmeticService[F[_]]
{ def add(value1: Int, value2: Int): F[Int] def subtract(value1: Int, value2: Int): F[Int] def multiply(value1: Int, value2: Int): F[Int] def divide(value1: Int, value2: Int): F[Int] } @tagless(true) trait ValidateService[F[_]] { def isPositive(value: Int): F[Boolean] def isZero(value: Int): F[Boolean] } 6 -
Freestyle's Workflow Freestyle's Workflow Declare and compose programs class ArithmeticProgram[F[_]
: ArithmeticService : ValidateService]( implicit ME: MonadError[F, Throwable]) { def program(value1: Int, value2: Int, value3: Int): F[Int] = for { result1 <- ArithmeticService[F].subtract(value1, value2) isZero <- ValidateService[F].isZero(result1) result2 <- if (isZero) ME.raiseError(new RuntimeException("Division by zero!")) else ArithmeticService[F].divide(value3, result1) } yield result2 } 7 -
Freestyle's Workflow Freestyle's Workflow Provide implicit evidence of your handlers
class ArithmeticServiceHandler[F[_]]( implicit ME: MonadError[F, Throwable]) extends ArithmeticService[F] { override def add(value1: Int, value2: Int): F[Int] = (value1 + value2).pure[F] override def subtract(value1: Int, value2: Int): F[Int] = (value1 - value2).pure[F] override def multiply(value1: Int, value2: Int): F[Int] = (value1 * value2).pure[F] override def divide(value1: Int, value2: Int): F[Int] = (value1 / value2).pure[F] } class ValidateServiceHandler[F[_]]( implicit ME: MonadError[F, Throwable]) extends ValidateService[F] { override def isPositive(value: Int): F[Boolean] = (value > 0).pure[F] override def isZero(value: Int): F[Boolean] = (value == 0).pure[F] } 8 -
Freestyle's Workflow Freestyle's Workflow Run your program to your desired
target object ArithmeticDemo extends App { type Target[A] = Either[Throwable, A] implicit val arithmeticServiceForTarget: ArithmeticServiceHandler[Target] = new ArithmeticServiceHandler[Target] implicit val validateServiceForTarget: ValidateServiceHandler[Target] = new ValidateServiceHandler[Target] new ArithmeticProgram[Target].program(4, 2, 6) // res0: Either[Throwable, Int] = Right(3) new ArithmeticProgram[Target].program(3, 3, 10) // res1: Either[Throwable, Int] = Left(java.lang.RuntimeException: Division by zero!) } 9 -
What is RPC (Remote Procedure Call) What is RPC (Remote
Procedure Call) Allow call methods in a remote app server as local Scala Service Scala client 11 -
gRPC gRPC open source high performance RPC framework Idiomatic client
libraries in several languages Bi-directional streaming with http/2 based transport 12 -
gRPC gRPC Main usage scenarios Connecting polyglot services in microservice-
based architecture Connecting mobile devices to backend services 13 -
gRPC gRPC Uses protocol buffers by default to: Define IDL:
service interfaces and structure of message Serialize/deserialize structure data 14 -
gRPC gRPC Define your proto message message Person { required
string name = 1; required int32 id = 2; optional string email = 3; } 15 -
gRPC gRPC Define your gRPC service service Greeter { rpc
SayHello (HelloRequest) returns (HelloReply); } message HelloRequest { string name = 1; } message HelloReply { string message = 1; } 16 -
Freestyle RPC Freestyle RPC frees-rpc is a FP layer atop
gRPC Messages and services defined in Scala files 17 -
Freestyle RPC Freestyle RPC Define your message @message case class
Person(name: String, id: Int, email: String) 18 -
Freestyle RPC Freestyle RPC Expose Algebras as RPC services @message
case class HelloRequest(name: String) @message case class HelloReply(message: String) @service trait Greeter[F[_]] { @rpc(Avro) def sayHello(request: HelloRequest): F[HelloReply] } 19 -
Freestyle RPC gives you for free: Freestyle RPC gives you
for free: Scala Service Scala client gRPC based server gRPC based client IDL files to interoperate with other langs. 20 -
Frestyle RPC Frestyle RPC IDL generation idlGen: sbt plugin Generation
of IDL files from Scala definition Generation of source files from IDL 21 -
Frestyle RPC Frestyle RPC Generation of IDL files from code
@message case class HelloRequest(greeting: String) @message case class HelloResponse(reply: String) @service trait Greeter[F[_]] { @rpc(Protobuf) def sayHello(request: HelloRequest): F[HelloResponse] @rpc(Avro) def sayHelloAvro(request: HelloRequest): F[HelloResponse] @rpc(Protobuf) @stream[ResponseStreaming.type] def lotsOfReplies(request: HelloRequest): Observable[HelloResponse] } syntax = "proto3"; option java_multiple_files = true; option java_outer_class_name = "Quickstart"; package quickstart; message HelloRequest { string greeting = 1; } message HelloResponse { string reply = 1; } service Greeter { rpc SayHello (HelloRequest) returns (HelloResponse); rpc LotsOfReplies (HelloRequest) returns (stream HelloResponse); } 22 -
Frestyle RPC Frestyle RPC Generation of IDL files from code
@message case class HelloRequest(greeting: String) @message case class HelloResponse(reply: String) @service trait Greeter[F[_]] { @rpc(Protobuf) def sayHello(request: HelloRequest): F[HelloResponse] @rpc(Avro) def sayHelloAvro(request: HelloRequest): F[HelloResponse] @rpc(Protobuf) @stream[ResponseStreaming.type] def lotsOfReplies(request: HelloRequest): Observable[HelloResponse] } { "namespace" : "quickstart", "protocol" : "GreeterService", "types" : [{ "name" : "HelloRequest", "type" : "record", "fields" : [{ "name" : "greeting", "type" : "string" }] }, { "name" : "HelloResponse", "type" : "record", "fields" : [{ "name" : "reply", "type" : "string" }] }], "messages" : { "sayHelloAvro" : { "request" : [{ "name" : "arg", "type" : "HelloRequest" } ], "response" : "HelloResponse" } } } 23 -
Frestyle RPC Frestyle RPC Generation of source files from IDL
Currently only Avro is supported Scala code from RPC services defined previously Prevents binary incompatibility 24 -
Service definition Service definition @message case class Point(latitude: Int, longitude:
Int) @message case class Rectangle(lo: Point, hi: Point) @message case class Feature(name: String, location: Point) @message case class RouteNote(location: Point, message: String) @message case class RouteSummary(points: List[Point], features: List[Feature], distance: Int) @service trait RouteGuideService[F[_]] { @rpc(Protobuf) def getFeature(point: Point): F[Feature] @rpc(Protobuf) @stream[ResponseStreaming.type] def listFeatures(rectangle: Rectangle): Observable[Feature] @rpc(Protobuf) @stream[RequestStreaming.type] def recordRoute(points: Observable[Point]): F[RouteSummary] @rpc(Protobuf) @stream[BidirectionalStreaming.type] def routeChat(routeNotes: Observable[RouteNote]): Observable[RouteNote] } 26 -
Service implementation Service implementation class RouteGuideServiceHandler[F[_] : Async](implicit S: Scheduler)
extends RouteGuideService[F] { override def getFeature(point: Point): F[Feature] = featureDatabase.getFeature(point).fold( Async[F].raiseError[Feature](new RuntimeException("Feature not found")))(Async[F].pure) override def listFeatures(rectangle: Rectangle): Observable[Feature] = Observable.fromIterable(featureDatabase.listFeatures(rectangle)) override def recordRoute(points: Observable[Point]): F[RouteSummary] = points.foldLeftL((List.empty[Point], List.empty[Feature])) { case ((visitedPoints, visitedFeatures), point) => val feature = featureDatabase.getFeature(point) (visitedPoints :+ point, visitedFeatures ++ feature.toList) } .map(summaryFromPointsAndFeatures.tupled.apply) .to[F] override def routeChat(routeNotes: Observable[RouteNote]): Observable[RouteNote] = routeNotes .flatMap { note: RouteNote => routeNoteDatabase.insertRouteNote(note) Observable.fromIterable(routeNoteDatabase.listRouteNotes(note.location)) } } 27 -
Building the server Building the server trait CommonRuntime { implicit
val S: Scheduler = monix.execution.Scheduler.Implicits.global } object gserver { trait Implicits extends CommonRuntime { implicit val routeGuideServiceHandler: RouteGuideService[IO] = new RouteGuideServiceHandler[IO] val rpcConfig: List[GrpcConfig] = List( AddService(RouteGuideService.bindService[IO]) ) implicit val serverW: ServerW = BuildServerFromConfig[IO]("rpc.server.port", rpcConfig).unsafeRunSync() } object implicits extends Implicits } 28 -
Running the server Running the server object ServerApp extends App
{ import gserver.implicits._ server[IO].unsafeRunSync() } 29 -
Building the client Building the client object gclient { trait
Implicits extends CommonRuntime with ClientConfig { implicit val routeGuideServiceClient: RouteGuideService.Client[Task] = RouteGuideService.client[Task](channelFor) } object implicits extends Implicits } 30 -
Running the client Running the client object ClientApp extends App
{ import gclient.implicits._ val logger = getLogger def processFeatures(features: Observable[Feature]): Task[Unit] = features .zipWithIndex .map { case (feature, i) => logger.info(s"Result #$i: $feature") } .completedL Await.result( processFeatures( routeGuideServiceClient.listFeatures( Rectangle(Point(400000000, -750000000), Point(420000000, -730000000)))).runAsync, Duration.Inf ) } 31 -
Frestyle RPC Frestyle RPC Other supported features Compression SSL/TLS encryption
Metrics reporting 32 -
Brought to you by Brought to you by [colin-passiv](https://github.com/colin-passiv) Adrián
Ramírez Fornell <[AdrianRaFo](https://github.com/AdrianRaFo)> Alejandro Gómez <[dialelo](https://github.com/dialelo)> Ana Mª Marquez <[anamariamv](https://github.com/anamariamv)> Andy Scott <[andyscott](https://github.com/andyscott)> Diego Esteban Alonso Blas <[diesalbla](https://github.com/diesalbla)> Domingo Valera <[dominv](https://github.com/dominv)> Fede Fernández <[fedefernandez](https://github.com/fedefernandez)> Francisco Diaz <[franciscodr](https://github.com/franciscodr)> Giovanni Ruggiero <[gruggiero](https://github.com/gruggiero)> Javi Pacheco <[javipacheco](https://github.com/javipacheco)> Javier de Silóniz Sandino <[jdesiloniz](https://github.com/jdesiloniz)> Jisoo Park <[guersam](https://github.com/guersam)> Jorge Galindo <[jorgegalindocruces](https://github.com/jorgegalindocruces)> Juan Pedro Moreno <[juanpedromoreno](https://github.com/juanpedromoreno)> Juan Ramón González <[jrgonzalezg](https://github.com/jrgonzalezg)> Lawrence Lavigne <[L-Lavigne](https://github.com/L-Lavigne)> Maureen Elsberry <[MaureenElsberry](https://github.com/MaureenElsberry)> Peter Neyens <[peterneyens](https://github.com/peterneyens)> Raúl Raja Martínez <[raulraja](https://github.com/raulraja)> Sam Halliday <[fommil](https://github.com/fommil)> Suhas Gaddam <[suhasgaddam](https://github.com/suhasgaddam)> ... and many more contributors 34