Scala Future & Promises

download Scala Future & Promises

If you can't read please download the document

Transcript of Scala Future & Promises

  1. 1. Asynchronous Programming with Future & Promises Asynchronous Programming with Future & Promises Satendra Kumar Software Consultant Knoldus Software LLP Satendra Kumar Software Consultant Knoldus Software LLP
  2. 2. Topics CoveredTopics Covered Future Callback Handler Functional Composition For-Comprehension Blocking Future Promises Live Demo Executor Services Choosing-executor service Best practices Future Callback Handler Functional Composition For-Comprehension Blocking Future Promises Live Demo Executor Services Choosing-executor service Best practices
  3. 3. import org.apache.commons.io.IOUtils import org.apache.http.client.methods.HttpGet import org.apache.http.impl.client.HttpClientBuilder object FacebookPage { private val homeURL = "https://graph.facebook.com/v2.0/" private val accessToken = "CAAVHCxa" private val url = s"$homeURL${"25"}/feed?method=GET&format=json&access_token=$ {accessToken}" def getFeeds(): String = { val request = new HttpGet(url) val client = HttpClientBuilder.create().build(); val response = client.execute(request) IOUtils.toString(response.getEntity().getContent()) } }
  4. 4. object FacebookFeedParser { implicit val formats = DefaultFormats def parse(facebookFeeds: String): List[FacebookPageFeed] = { val parsedFacebookFeeds = net.liftweb.json.parse(facebookFeeds) (parsedFacebookFeeds"data").children map { facebookfeed => val contentId = (facebookfeed"id") val date = (facebookfeed"created_time") val content = (facebookfeed"message") FacebookPageFeed(contentId, date, content) } } implicit def extract(json: JValue): String = json match { case JNothing => "" case data => data.extract[String] } } case class FacebookPageFeed(id: String, date: String, content: String)
  5. 5. object DemoApp extends App { val facebookFeeds = FacebookPage.getFeeds() val parsedFacebookFeed = FacebookFeedParser.parse(facebookFeeds) val result = FacebookRepository.save(parsedFacebookFeed) // do some other tasks }
  6. 6. object DemoApp extends App { val facebookFeeds = FacebookPage.getFeeds() val parsedFacebookFeed = FacebookFeedParser.parse(facebookFeeds) val result = FacebookRepository.save(parsedFacebookFeed) // do some other tasks } Something wrong ?
  7. 7. object DemoApp extends App { val facebookFeeds = FacebookPage.getFeeds() val parsedFacebookFeed = FacebookFeedParser.parse(facebookFeeds) val result = FacebookRepository.save(parsedFacebookFeed) // do some other tasks } This is blocking
  8. 8. Scala's Futures API
  9. 9. Scala's Futures API Futures provide a nice way to reason about performing many operations in parallel in an efficient and non-blocking way.
  10. 10. Scala's Futures API Futures provide a nice way to reason about performing many operations in parallel in an efficient and non-blocking way. The idea is simple, a Future is a sort of a placeholder object that you can create for a result that does not yet exist. Generally, the result of the Future is computed concurrently and can be later collected. Composing concurrent tasks in this way tends to result in faster, asynchronous, non-blocking parallel code.
  11. 11. Scala's Futures API Futures provide a nice way to reason about performing many operations in parallel in an efficient and non-blocking way. The idea is simple, a Future is a sort of a placeholder object that you can create for a result that does not yet exist. Generally, the result of the Future is computed concurrently and can be later collected. Composing concurrent tasks in this way tends to result in faster, asynchronous, non-blocking parallel code. By default, futures and promises are non-blocking, making use of callbacks instead of typical blocking operations.
  12. 12. Scala's Futures API Futures provide a nice way to reason about performing many operations in parallel in an efficient and non-blocking way. The idea is simple, a Future is a sort of a placeholder object that you can create for a result that does not yet exist. Generally, the result of the Future is computed concurrently and can be later collected. Composing concurrent tasks in this way tends to result in faster, asynchronous, non-blocking parallel code. By default, futures and promises are non-blocking, making use of callbacks instead of typical blocking operations. Blocking is still possible - for cases where it is absolutely necessary, futures can be blocked on (although this is discouraged).
  13. 13. import scala.concurrent.Future import scala.concurrent.ExecutionContext.Implicits.global import com.knol.facebook.FacebookPage object DemoApp extends App { val facebookFeeds: Future[String] = Future { FacebookPage.getFeeds() } }
  14. 14. def apply[T](body: =>T)(implicit executor: ExecutionContext): Future[T] Future's apply method
  15. 15. Implicit Execution Context object Implicits { implicit lazy val global: ExecutionContextExecutor = impl.ExecutionContextImpl.fromExecutor(null: Executor) }
  16. 16. Explicit Execution Context We can specify own ExecutionContext , instead of importing the global implicit ExecutionContext. import java.util.concurrent.Executors import scala.concurrent.ExecutionContext import scala.concurrent.Future import com.knol.facebook.FacebookPage import scala.concurrent.ExecutionContextExecutor import java.util.concurrent.ExecutorService object DemoApp extends App { val noOfThread = 10 val threadPool:ExecutorService = Executors.newFixedThreadPool(noOfThread) val ec: ExecutionContextExecutor = ExecutionContext.fromExecutor(threadPool) val facebookFeeds = Future { FacebookPage.getFeeds() }(ec) }
  17. 17. import scala.concurrent.Future import scala.concurrent.ExecutionContext.Implicits.global import com.knol.facebook.FacebookPage object DemoApp extends App { val facebookFeeds: Future[String] = Future { FacebookPage.getFeeds() } } Future completion can take one of two forms:
  18. 18. import scala.concurrent.Future import scala.concurrent.ExecutionContext.Implicits.global import com.knol.facebook.FacebookPage object DemoApp extends App { val facebookFeeds: Future[String] = Future { FacebookPage.getFeeds() } } Future completion can take one of two forms: a)When a Future is completed with a value, we say that the future was successfully completed with that value.
  19. 19. import scala.concurrent.Future import scala.concurrent.ExecutionContext.Implicits.global import com.knol.facebook.FacebookPage object DemoApp extends App { val facebookFeeds: Future[String] = Future { FacebookPage.getFeeds() } } Future completion can take one of two forms: a)When a Future is completed with a value, we say that the future was successfully completed with that value. b)When a Future is completed with an exception thrown by the computation, we say that the Future was failed with that exception.
  20. 20. Callbacks
  21. 21. Callbacks We now know how to start an asynchronous computation to create a new future value, but we have not shown how to use the result once it becomes available, so that we can do something useful with it.
  22. 22. Callbacks We now know how to start an asynchronous computation to create a new future value, but we have not shown how to use the result once it becomes available, so that we can do something useful with it. We can get result by registering a callback on the future.
  23. 23. Callbacks We now know how to start an asynchronous computation to create a new future value, but we have not shown how to use the result once it becomes available, so that we can do something useful with it. We can get result by registering a callback on the future. This callback is called asynchronously once the future is completed. If the future has already been completed when registering the callback, then the callback may either be executed asynchronously, or sequentially on the same thread.
  24. 24. Callbacks def onSuccess[U](pf: PartialFunction[T, U])(implicit executor:ExecutionContext): Unit def onFailure[U](pf:PartialFunction[Throwable,U])(implicit executor:ExecutionContext): Unit def onComplete[U](f: Try[T] => U)(implicit executor: ExecutionContext): Unit
  25. 25. import scala.concurrent.Future import scala.concurrent.ExecutionContext.Implicits.global import com.knol.facebook.FacebookPage object DemoApp extends App { val facebookFeeds: Future[String] = Future { FacebookPage.getFeeds() } facebookFeeds onSuccess { case feeds => println(feeds) } } onSuccess
  26. 26. onFailure object DemoApp extends App { val facebookFeeds: Future[String] = Future { FacebookPage.getFeeds() } facebookFeeds onFailure { case feeds:Throwable => println("Error getting feeds "+feeds.getMessage) } }
  27. 27. onComplete import scala.concurrent.Future import scala.concurrent.ExecutionContext.Implicits.global import com.knol.facebook.FacebookPage import scala.util.Success import scala.util.Failure object DemoApp extends App { val facebookFeeds: Future[String] = Future { FacebookPages.getFeeds() } facebookFeeds onComplete { case Success(feeds) => println("feeds " + feeds) case Failure(ex) => println("Error "+ex.getMessage) } }
  28. 28. Callbacks Callbacks registered on the same future are unordered. There is no guarantee it will be called by the thread that completed the future or the thread which created the callback. Instead, the callback is executed by some thread, at some time after the future object is completed. Registering a callback on the future which is already completed will result in the callback being executed eventually. Once executed, the callbacks are removed from the future object, thus being eligible for GC
  29. 29. Functional Composition Futures provide combinators which allow a more straightforward composition. def map[S](f: T => S)(implicit executor: ExecutionContext): Future[S] def flatMap[S](f: T => Future[S])(implicit executor: ExecutionContext): Future[S] def recover[U >: T](pf: PartialFunction[Throwable, U])(implicit ec: ExecutionContext): Future[U] def recoverWith[U >: T](pf: PartialFunction[Throwable,Future[U]])(implicit ec: ExecutionContext):Future[U] def foreach[U](f: T => U)(implicit executor: ExecutionContext): Unit def fallbackTo[U >: T](that: Future[U]): Future[U] def andThen[U](pf: PartialFunction[Try[T], U])(implicit executor: ExecutionContext): Future[T] def mapTo[S](implicit tag: ClassTag[S]): Future[S] def sequence[A, M[X] FacebookFeedParser.parse(feed) } feedList onComplete { case Success(feeds) => feeds foreach println case Failure(ex) => println("Error getting feed " + ex.getMessage) } }
  30. 31. flatMap import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.Future import scala.util.Failure import scala.util.Success import com.knol.facebook.FacebookPage import com.knol.facebook.FacebookFeedParser import com.knol.facebook.FacebookPageFeed object DemoApp extends App { val facebookFeeds: Future[String] = Future { FacebookPage.getFeeds() } val feedList: Future[List[FacebookPageFeed]] = facebookFeeds flatMap { feed => FacebookFeedParser.parse(feed) } feedList onComplete { case Success(feeds) => feeds foreach println case Failure(ex) => println("Error getting feed " + ex.getMessage) } }
  31. 32. foreach import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.Future import scala.util.Failure import scala.util.Success import com.knol.facebook.FacebookPage import com.knol.facebook.FacebookFeedParser import com.knol.facebook.FacebookPageFeed object DemoApp extends App { val facebookFeeds: Future[String] = Future { FacebookPage.getFeeds() } val feedList: Future[List[FacebookPageFeed]] = facebookFeeds flatMap { feed => FacebookFeedParser.parse(feed) } feedList foreach println }
  32. 33. recover import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.Future import scala.util.Failure import scala.util.Success import com.knol.facebook.FacebookPage import com.knol.facebook.FacebookFeedParser import com.knol.facebook.FacebookPageFeed object DemoApp extends App { val facebookFeeds: Future[String] = Future { FacebookPage.getFeeds() } val feedList: Future[List[FacebookPageFeed]] = facebookFeeds map { feed => FacebookFeedParser.parse(feed) } recover { case ex: Throwable => List() } feedList onComplete { case Success(feeds) => feeds foreach println case Failure(ex) => println("Error getting feed " + ex.getMessage) } }
  33. 34. recoverWith import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.Future import scala.util.Failure import scala.util.Success import com.knol.facebook.FacebookPage import com.knol.facebook.FacebookFeedParser import com.knol.facebook.FacebookPageFeed object DemoApp extends App { val facebookFeeds: Future[String] = Future { FacebookPage.getFeeds() } val feedList: Future[List[FacebookPageFeed]] = facebookFeeds flatMap { feed => FacebookFeedParser.parse(feed) } recoverWith { case ex: Throwable => Future { List(FacebookPageFeed("", "", "")) } } feedList onComplete { case Success(feeds) => feeds foreach println case Failure(ex) => println("Error getting feed " + ex.getMessage) } }
  34. 35. fallbackTo import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.Future import scala.util.Failure import scala.util.Success import com.knol.facebook.FacebookPage import com.knol.facebook.FacebookFeedParser import com.knol.facebook.FacebookPageFeed object DemoApp extends App { val facebookFeeds: Future[String] = Future { FacebookPage.getFeeds() } val fallbackFuture: Future[String] = Future { "{}" } val resultantFuture = facebookFeeds fallbackTo fallbackFuture resultantFuture onComplete { case Success(feeds) => feeds foreach println case Failure(ex) => println("Error getting feed " + ex.getMessage) } }
  35. 36. andThen The andThen combinator is used purely for side-effecting purposes. It returns a new future with exactly the same result as the current future, regardless of whether the current future failed or not. Once the current future is completed with the result, the closure corresponding to the andThen is invoked and then the new future is completed with the same result as this future import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.Future import scala.util._ import com.knol.facebook.FacebookPage import com.knol.facebook.FacebookFeedParser object DemoApp extends App { val facebookFeeds: Future[String] = Future { FacebookPage.getFeeds() } val resultantFuture = facebookFeeds andThen { case Success(feed) => FacebookFeedParser.parse(feed) foreach println } resultantFuture onComplete { case Success(feeds) => feeds foreach println case Failure(ex) => println("Error getting feed " + ex.getMessage) } }
  36. 37. sequence val listOfFuture: List[Future[Int]] = List(Future(2 + 2), Future(20 + 3), Future(2 + 347)) val combindFuture: Future[List[Int]] = Future.sequence(listOfFuture)
  37. 38. For-Comprehensions import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.Future import com.knol.facebook.FacebookPage import com.knol.facebook.FacebookFeedParser import com.knol.facebook.FacebookPageFeed import scala.concurrent.Await import scala.concurrent.duration._ import scala.util.Success import scala.util.Failure object DemoApp extends App { val facebookFeeds: Future[String] = Future { println("Current Thread in future block::::::" + Thread.currentThread().getName) FacebookPage.getFeeds() } val future = for { feeds println("Error:::::::::::" + ex.getMessage) } }
  38. 39. Blocking blocking on a future is strongly discouraged for the sake of performance and for the prevention of deadlocks. Callbacks and combinators on futures are a preferred way to use their results. However, blocking may be necessary in certain situations and is supported by the Futures and Promises API. import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.Future import com.knol.facebook.FacebookPage import com.knol.facebook.FacebookFeedParser import com.knol.facebook.FacebookPageFeed import scala.concurrent.Await import scala.concurrent.duration._ object DemoApp extends App { val facebookFeeds: Future[String] = Future { FacebookPage.getFeeds() } val result = Await.result(facebookFeeds, 10 seconds) println(result) }
  39. 40. Promises
  40. 41. Promises Promise is an object which can be completed with a value or failed with an exception.
  41. 42. Promises Promise is an object which can be completed with a value or failed with an exception. While futures are defined as a type of read-only placeholder object created for a result which doesnt yet exist, a promise can be thought of as a writable, single-assignment container, which completes a future. That is, a promise can be used to successfully complete a future with a value (by completing the promise) using the success method. Conversely, a promise can also be used to complete a future with an exception, by failing the promise, using the failure method.
  42. 43. Promises Promise is an object which can be completed with a value or failed with an exception. While futures are defined as a type of read-only placeholder object created for a result which doesnt yet exist, a promise can be thought of as a writable, single-assignment container, which completes a future. That is, a promise can be used to successfully complete a future with a value (by completing the promise) using the success method. Conversely, a promise can also be used to complete a future with an exception, by failing the promise, using the failure method. A promise p completes the future returned by p.future. This future is specific to the promise p. Depending on the implementation, it may be the case that p.future eq p.
  43. 44. import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.Future import com.knol.facebook.FacebookPage import scala.concurrent.duration._ import scala.util.Success import scala.util.Failure import scala.concurrent.Promise object PromiseDemo extends App { val promise = Promise[String] val future = promise.future Future { val feeds = FacebookPage.getFeeds() promise success feeds } future onComplete { case Success(feeds) => println("Feeds:::::::::::" + feeds) case Failure(ex) => println("Error:::::::::::" + ex.getMessage) } }
  44. 45. import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.Future import scala.concurrent.Promise import com.knol.facebook.FacebookPage object PromiseDemo { def asynExecute: Future[String] = { val promise = Promise[String] Future { //do other task val feeds = FacebookPage.getFeeds() promise success feeds //do other task } promise.future } }
  45. 46. 1) Write a method that return a future that is never completed
  46. 47. 1) Write a method that return a future that is never completed def never[T]: Future[T] = Promise[T].future
  47. 48. 1) Write a method that return a future that is never completed def never[T]: Future[T] = Promise[T].future 2) Write a method that return a future with a unit value that is completed after time `t`
  48. 49. 1) Write a method that return a future that is never completed def never[T]: Future[T] = Promise[T].future 2) Write a method that return a future with a unit value that is completed after time `t` def delay(t: Duration): Future[Unit] = Future { Try(Await.ready(Promise().future, t)) }
  49. 50. Executor Services FixedThreadPool CachedThreadPool SingleThreadExecutor ForkJoinPool
  50. 51. FixedThreadPool It is fixed size thread pool Executors utility class have factory methods for creating FixedThreadPool: 1) static ExecutorService newFixedThreadPool(int nThreads) Creates a thread pool that reuses a fixed number of threads operating off a shared unbounded queue. 2) static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) Creates a thread pool that reuses a fixed number of threads operating off a shared unbounded queue, using the provided ThreadFactory to create new threads when needed.
  51. 52. CachedThreadPool It is unbounded thread pool, with automatic thread reclamation Executors utility class have factory methods for creating CachedThreadPool: 1) static ExecutorService newCachedThreadPool() Creates a thread pool that creates new threads as needed, but will reuse previously constructed threads when they are available. 2) static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) Creates a thread pool that creates new threads as needed, but will reuse previously constructed threads when they are available, and uses the provided ThreadFactory to create new threads when needed.
  52. 53. ForkJoinPool A ForkJoinPool is an ExecutorService that recognizes explicit dependencies between tasks. It is designed for the kind of computation that wants to run in parallel, and then maybe more parallel, and then some of those can run in parallel too. Results of the parallel computations are then combined, so it's like the threads want to split off and then regroup. A ForkJoinPool differs from other kinds of ExecutorService mainly by virtue of employing work-stealing: all threads in the pool attempt to find and execute subtasks created by other active tasks (eventually blocking waiting for work if none exist). This enables efficient processing when most tasks spawn other subtasks (as do most ForkJoinTasks). When setting asyncMode to true in constructors, ForkJoinPools may also be appropriate for use with event-style tasks that are never joined. Implementation notes: This implementation restricts the maximum number of running threads to 32767. Attempts to create pools with greater than the maximum number result in IllegalArgumentException.
  53. 54. ForkJoinPool //Creates a ForkJoinPool with parallelism equal to Runtime.availableProcessors(), using the default thread factory val mainPool: ForkJoinPool = new ForkJoinPool() //Creates a ForkJoinPool with the indicated parallelism level, the default thread factory, val mainPoolWithParallelism: ForkJoinPool = new ForkJoinPool(50)
  54. 55. Choosing-executorservice http://blog.jessitron.com/2014/01/choosing-executorservice.html
  55. 56. Best practices How you should best divide work in your application between different thread pools greatly depends on the types of work that your application is doing, and the control you want to have over how much of which work can be done in parallel. There is no one size fits all solution to the problem, and the best decision for you will come from understanding the blocking-IO requirements of your application and the implications they have on your thread pools. It may help to do load testing on your application to tune and verify your configuration.
  56. 57. Best practices object Contexts { val noOfThreadForDBOperation = 10 // set according to application load implicit val simpleDbLookups: ExecutionContext = ExecutionContext.fromExecutor(Executors.newCachedThreadPool()) implicit val expensiveDbLookups: ExecutionContext = ExecutionContext.fromExecutor(Executors.newFixedThreadPool(noOfThreadForDBOperation)) implicit val expensiveCpuOperations: ExecutionContext = ExecutionContext.fromExecutor(new ForkJoinPool()) }
  57. 58. Plays thread pools Play2.3 uses a number of different thread pools for different purposes: Netty boss/worker thread pools - These are used internally by Netty for handling Netty IO. An applications code should never be executed by a thread in these thread pools. Play Internal Thread Pool - This is used internally by Play. No application code should ever be executed by a thread in this thread pool, and no blocking should ever be done in this thread pool. Its size can be configured by setting internal-threadpool-size in application.conf, and it defaults to the number of available processors. Play default thread pool - This is the default thread pool in which all application code in Play Framework is executed. It is an Akka dispatcher, and can be configured by configuring Akka. By default, it has one thread per processor. Akka thread pool - This is used by the Play Akka plugin, and can be configured the same way that you would configure Akka.
  58. 59. Plays thread pools Play2.4 uses a number of different thread pools for different purposes: Netty boss/worker thread pools - These are used internally by Netty for handling Netty IO. An applications code should never be executed by a thread in these thread pools. Play default thread pool - This is the thread pool in which all of your application code in Play Framework is executed. It is an Akka dispatcher, and is used by the application ActorSystem. It can be configured by configuring Akka, described below. Note that in Play 2.4 several thread pools were combined together into the Play default thread pool.
  59. 60. Plays thread pools In most situations, the appropriate execution context to use will be the Play default thread pool. This can be used by importing it into your Scala source file: //in play import play.api.libs.concurrent.Execution.Implicits._ // in scala import scala.concurrent.ExecutionContext.Implicits._
  60. 61. Best practices object Contexts { implicit val simpleDbLookups: ExecutionContext = Akka.system.dispatchers.lookup("contexts.simple-db-lookups") implicit val expensiveDbLookups: ExecutionContext = Akka.system.dispatchers.lookup("contexts.expensive-db-lookups") implicit val dbWriteOperations: ExecutionContext = Akka.system.dispatchers.lookup("contexts.db-write-operations") implicit val expensiveCpuOperations: ExecutionContext = Akka.system.dispatchers.lookup("contexts.expensive-cpu-operations") } These might then be configured like so: contexts { simple-db-lookups { fork-join-executor { parallelism-factor = 10.0 } } expensive-db-lookups { fork-join-executor { parallelism-max = 4 } } db-write-operations { fork-join-executor { parallelism-factor = 2.0 } } expensive-cpu-operations { fork-join-executor { parallelism-max = 2 } } }
  61. 62. References Futures and Promises http://blog.jessitron.com/2014/01/choosing-executorservice.html http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ForkJo https://www.playframework.com/documentation/2.3.x/ThreadPools https://www.playframework.com/documentation/2.4.x/ThreadPools