Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Introduction to freestyle-rpc

Introduction to freestyle-rpc

Francisco Diaz

April 18, 2018
Tweet

Transcript

  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
  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
  3. In this talk In this talk Freestyle programming style RPC

    based Microservices gRPC Freestyle RPC 4
  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
  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
  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
  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
  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
  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
  10. gRPC gRPC open source high performance RPC framework Idiomatic client

    libraries in several languages Bi-directional streaming with http/2 based transport 12
  11. gRPC gRPC Main usage scenarios Connecting polyglot services in microservice-

    based architecture Connecting mobile devices to backend services 13
  12. gRPC gRPC Uses protocol buffers by default to: Define IDL:

    service interfaces and structure of message Serialize/deserialize structure data 14
  13. gRPC gRPC Define your proto message message Person { required

    string name = 1; required int32 id = 2; optional string email = 3; } 15
  14. gRPC gRPC Define your gRPC service service Greeter { rpc

    SayHello (HelloRequest) returns (HelloReply); } message HelloRequest { string name = 1; } message HelloReply { string message = 1; } 16
  15. Freestyle RPC Freestyle RPC frees-rpc is a FP layer atop

    gRPC Messages and services defined in Scala files 17
  16. Freestyle RPC Freestyle RPC Define your message @message case class

    Person(name: String, id: Int, email: String) 18
  17. 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
  18. 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
  19. Frestyle RPC Frestyle RPC IDL generation idlGen: sbt plugin Generation

    of IDL files from Scala definition Generation of source files from IDL 21
  20. 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
  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] } { "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
  22. 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
  23. 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
  24. 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
  25. 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
  26. Running the server Running the server object ServerApp extends App

    { import gserver.implicits._ server[IO].unsafeRunSync() } 29
  27. 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
  28. 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
  29. 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