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. 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. 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. object DemoApp extends App { val facebookFeeds =
FacebookPage.getFeeds() val parsedFacebookFeed =
FacebookFeedParser.parse(facebookFeeds) val result =
FacebookRepository.save(parsedFacebookFeed) // do some other tasks
}
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. 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. Scala's Futures API
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. 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. 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. 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).
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. 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. 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. 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. Callbacks
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. 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. 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. 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
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. 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) } }
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) } }
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)
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) }
40. Promises
41. Promises Promise is an object which can be completed with a
value or failed with an exception.
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.
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.
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 } }
46. 1) Write a method that return a future that is never
completed
47. 1) Write a method that return a future that is never
completed def never[T]: Future[T] = Promise[T].future
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`
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)) }
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.
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.
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.
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)
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.
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()) }
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.
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.
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._
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 } } }
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