[ScalaByTheBay2016] Implement a scalable statistical aggregation system using Akka
Building scalable rest service using Akka HTTP
-
Upload
datamantra -
Category
Software
-
view
750 -
download
4
Transcript of Building scalable rest service using Akka HTTP
Introduction to Akka HTTPBuilding scalable rest service in Scala
https://github.com/shashankgowdal/introduction-to-akkahttp
● Shashank L
● Senior Software engineer at Tellius
● Part time big data consultant and trainer at datamantra.io
● www.shashankgowda.com
Agenda● Motivation● Akka, Reactive streams● Akka Http● Akka Http High level API● Testing● Additional functionality● Working with files● Websockets● Akka Http with Spark
Scala frameworks to build REST API● Play/Lift
○ Full stack web framework● Scalatra
○ Lightweight but requires a application container for deployment
● Spray○ Lightweight and robust as it's asynchronous and built on top
of Actors● Akka HTTP
○ Next version of Spray framework
Why Akka HTTP● Its 2.0 version of Spray.io
● Internals of Spray have been rewritten to use
Reactive streams
● Reactive streams idea gel well with earlier ideas of
Spray
● Multiple level API
Akka● Toolkit and runtime for building highly concurrent,
distributed, and fault tolerant applications on the JVM
● Uses message passing based concurrency model
● Written in Scala
● Scale up or out
● Program at higher level
● Distributable by design
● Message passing is built on top of AkkaActor model
Akka actor● Receives message and takes action to handle them● Consists of
○ Behaviour○ State○ Mailbox
Reactive streams● Asynchronous● Stream processing● Back-pressured● Interoperability● Few Implementations
○ Akka Streams○ RxJava○ Play○ Kafka
● Spark streaming with Kafka receiver
Akka streams
● Akka’s implementation of Reactive streams
● DSL for building a complete stream
● Higher level abstraction over the actor model
● Stream inputs and outputs are typesafe
● Internally uses Actor to implement Reactive stream
properties
Source, Flow and Sink
com.shashank.akkahttp.IntroductionToStream
implicit val sys = ActorSystem("IntroductionToStream")implicit val mat:Materializer = ActorMaterializer()
val source = Source(List(1, 2, 3))
val flow = Flow[Int].map(_.toString)
val sink = Sink.foreach(println)
val runnableGraph = source via flow to sinkrunnableGraph.run()
HTTP server with Akka streams
HTTPServer as a:Flow[HttpRequest, HttpResponse]
Socket input
Bytes ⇒ HttpRequest
HttpRequest ⇒ HttpResponse
HttpResponse ⇒ Bytes
Socket Sink
Akka Http server using Flow
implicit val sys = ActorSystem("IntroductionToAkkaHttp")implicit val mat:Materializer = ActorMaterializer()
val requestResponseFlow = Flow.fromFunction[HttpRequest, HttpResponse]( request => {println(request.toString)HttpResponse(StatusCodes.OK, entity = "Hello!")
})
Http().bindAndHandle(requestResponseFlow, "localhost", 8080)
com.shashank.akkahttp.basic.serving.StreamsServing
Akka HTTP
● Part of Akka project
● HTTP toolkit (Client and Server)
● On top of akka-actor and akka-streams
● Multiple levels of abstraction - Open API
● Scalable, Max throughput, acceptable latency
● APIs in both Scala and Java
Akka HTTP - Implementation
● Fully asynchronous and nonblocking
● Focused on higher level API
● Lightweight
● Modular
● Testable
● Based on spray.io
● Better streaming REST functionality support
Akka HTTP structure
Akka HTTP module
akka-http
akka-http-core
akka-http-testkit
akka-http-spray-json
akka-http-xml
Akka HTTP - Parts● akka-http-core
○ low-level server & client side HTTP implementation
● akka-http○ high-level API : DSL, (un)marshalling, (de)compression
● akka-http-testkit○ Utilities for testing server-side implementation
● akka-http-spray-json○ (de)serialization from/to JSON with spray-json
● akka-http-xml○ (de)serialization from/to XML with scala-xml
HTTP model● Fully immutable, case-class based
● Abstraction for most HTTP things (Types!)
● Little logic inside
● Lots of predefined types - media type, status code, encodings etc
● Efficient parsing and rendering
● Clients/Connected services should obey the standard
HTTP model - Examplescase class HttpRequest(method: HttpMethod = HttpMethods.GET, uri: Uri = Uri./, headers: immutable.Seq[HttpHeader] = Nil, entity: RequestEntity = HttpEntity.Empty, protocol: HttpProtocol = HttpProtocols.`HTTP/1.1`)
case class HttpResponse(status: StatusCode = StatusCodes.OK, headers: immutable.Seq[HttpHeader] = Nil, entity: ResponseEntity = HttpEntity.Empty, protocol: HttpProtocol = HttpProtocols.`HTTP/1.1`)
case class Uri(scheme: String, authority: Authority, path: Path, rawQueryString: Option[String], fragment: Option[String])
HTTP model - Examplesobject ContentTypes {
val `application/json` = ContentType(MediaTypes.`application/json`)val `application/octet-stream` = ContentType(MediaTypes.`application/octet-stream`)
val `text/plain(UTF-8)` = MediaTypes.`text/plain` withCharset HttpCharsets.`UTF-8` val `text/html(UTF-8)` = MediaTypes.`text/html` withCharset HttpCharsets.`UTF-8` val `text/xml(UTF-8)` = MediaTypes.`text/xml` withCharset HttpCharsets.`UTF-8`
val `text/csv(UTF-8)` = MediaTypes.`text/csv` withCharset HttpCharsets.`UTF-8` val NoContentType = ContentType(MediaTypes.NoMediaType)}
val `US-ASCII` = register("US-ASCII")("iso-ir-6", "ANSI_X3.4-1968", "ANSI_X3.4-1986", "ISO_646.irv:1991", "ASCII", "ISO646-US", "us", "IBM367", "cp367", "csASCII")val `ISO-8859-1` = register("ISO-8859-1")("iso-ir-100", "ISO_8859-1", "latin1", "l1", "IBM819", "CP819", "csISOLatin1")val `UTF-8` = register("UTF-8")("UTF8")val `UTF-16` = register("UTF-16")("UTF16")val `UTF-16BE` = register("UTF-16BE")()val `UTF-16LE` = register("UTF-16LE")()
HTTP server functional way● Low level HTTP API● Provided by akka-http-core-moduleimplicit val sys = ActorSystem("IntroductionToAkkaHttp")implicit val mat:Materializer = ActorMaterializer()
val handler :(HttpRequest => HttpResponse) = {case HttpRequest(HttpMethods.GET, Uri.Path("/ping"), _, _, _) =>
HttpResponse(StatusCodes.OK, entity = "pong!") case r => HttpResponse(status = StatusCodes.BadRequest)}Http().bindAndHandleSync(handler, "localhost", 8080)
com.shashank.akkahttp.basic.serving.FunctionalServing
HTTP Server Nice way!
● Low level API becomes uneasy to handle when we need large number of routes
● For this we should use higher level API by akka-http● Route using Routing DSLimplicit val sys = ActorSystem("IntroductionToAkkaHttp")implicit val mat:Materializer = ActorMaterializer()
val routes: Route = ???Http(sys).bindAndHandle(route, "localhost", 8090)
Routing DSL
● Internal domain specific language for routing● How most services are actually written● Layer to the application● Type safe but flexible● Not just routing - behaviour definition● Very composable● Fun, powerful and looks clean
Routing DSL - Exampleval route =path("welcome"){ get{ complete { "welcome to rest service" } }} ~path("demo"){ get{ complete { "welcome to demonstration" } }}Http().bindAndHandle(route, "localhost", 8090)
com.shashank.akkahttp.basic.routing.RoutingDSL
Directives● A Directive is a small building block used for creating
routes.● There are some predefined directives( get, post,
complete etc.)● We can also define our custom directives.● Roles: extracting, transforming request or response,
filtering, side-effecting● ~136 predefined directives
Rejections
● Produced by directives● Travel down the routing structure● EmptyRejection is thrown when no route completes● Can be extended● Can be cancelled● Handling rejection = transforming it to response● Ex. MethodRejection, AuthorizationFailedRejection,MissingCookieRejection, MissingQueryParamRejection
Rejections● ~ operator connects two routes in a way that allows a
second route to get a go at a request if the first route "rejected" it.
path("order") { get { complete("Received GET") } ~ post { complete("Received POST") }}
com.shashank.akkahttp.basic.routing.Rejection
Failures
● Are triggered by exceptions● Travel up the routing structure● Can be handled by handleExceptions directive or
top-level ExceptionHandler● Can be used to simplify the flow for validation
purpose
com.shashank.akkahttp.basic.routing.Failure
● Simple and straightforward● Allows to assert responses returned for given
requests● Integrates well with ScalatestGet("/ping") ~> route ~> check{ status === OK entity.as[String] === "It Works!"}
com.shashank.akkahttp.basic.routing.TestKit
Testkit
Query parameters
def parameters(param: <ParamDef[T]>): Directive1[T]
● Mandatory parameters● Optional parameters● Parameters with required value● Deserialized parameter● Repeated parameter● Deserialized parameter into Case class
(Un)Marshalling
● Marshalling - high level → low (wire) level
● Unmarshalling - low (wire) level → high level
● Many predefined (un) marshallers are available
● Extensible
● JSON and XML supported
● Type-class approach - Implicit resolution
Custom Http entity data
● Akka Http JSON support● Support conversion to and from JVM objects to wire
representation like JSON, XML etc● SprayJsonSupport provides a
FromEntityUnmarshaller[T] and ToEntityMarshaller[T] for every type T with Spray Json Reader/Writer
com.shashank.akkahttp.basic.routing.CustomEntityWithJson
Custom directive
● Configuration labelling● Transforming existing directives● Directive0, Directive1● Authorization● Authentication
com.shashank.akkahttp.basic.routing.CustomDirective
File uploaddef uploadedFile(fieldName: String): Directive1[(FileInfo, File)]
● Streams the contents of a file uploaded as a multipart form into a temporary file on disk
● Cannot start processing the file unless it written completely to temporary file
com.shashank.akkahttp.basic.routing.FileUpload
File upload streamdef fileUpload(fieldName: String): Directive1[(FileInfo, Source[ByteString, Any])]
● Simple access to the stream of bytes for a file uploaded as a multipart form together with metadata about the upload as extracted value.
com.shashank.akkahttp.basic.routing.FileUploadStream
Websocket● WebSocket is a protocol that provides a bi-directional channel
between browser and webserver● Data is exchanged in messages whereby a message can either
be binary data or unicode text
Server side websocket in AkkaHttp● Akka HTTP provides a stream-based implementation of the
WebSocket protocol● basic unit of data exchange in the WebSocket is a message i.e
TextMessage or BinaryMessage● Websocket handshake is managed and hidden from application
layer● A message handler is expected to be implemented as a
Flow[Message, Message, Any]● Testing Websocket using WSProbe
com.shashank.akkahttp.basic.routing.Websocket
Pros and Cons
● Pros○ Backed by Lightbend(Typesafe)○ Integration layers○ Microservices○ Pure REST APIs
● Cons○ Full fledged web applications (server side template generation)
○ Not mature enough○ Easy to start, hard to master
References
● Akka HTTP - A reactive web toolkit● Akka HTTP - What, Why and How● Introduction to Akka HTTP● Akka HTTP documentation
http://doc.akka.io/docs/akka/2.4.2/scala/http/
● http://blog.madhukaraphatak.com/categories/akka-http/