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

Java Developer Day 2013 Scala Future API

yunglin
August 02, 2013

Java Developer Day 2013 Scala Future API

yunglin

August 02, 2013
Tweet

More Decks by yunglin

Other Decks in Technology

Transcript

  1. The Challenge • The clock speed has stopped growing since

    2006 • The free lunch is over • Moore’s Law still applies but only the number of cores in a single chip is increasing. • The new reality: Amdahl's Law. ref: http://en.wikipedia.org/wiki/Amdahl's_law 2 Friday, August 2, 13
  2. Traditional Model • IO 5 Green: Thread in good use

    Red: Thread resource wasted Friday, August 2, 13
  3. Traditional Model • IO 7 Green: Thread in good use

    Red: Thread resource wasted Friday, August 2, 13
  4. 8 • stack size: – 320k on 32bit x86 JVM

    – 1024k on 64bit x86 JVM • scheduling cost. thread creation is expensive! Friday, August 2, 13
  5. 250,000,000 cs • c10k - 25,000 boxes • c100k -

    2,500 boxes 11 Friday, August 2, 13
  6. 250,000,000 cs • c10k - 25,000 boxes • c100k -

    2,500 boxes • c1Mk - 250 boxes • c2Mk - 125 boxes 12 Friday, August 2, 13
  7. Async, Event Driven Model 13 note: In reality, the callback

    will be invoked on another thread. Friday, August 2, 13
  8. Java5 Future API 15 1 public interface Callable <V> {

    2 V call() throws java.lang.Exception; 3 } 4 5 public interface ExecutorService extends Executor { 6 <T> Future<T> submit(Callable<T> tCallable); 7 } 8 9 public interface Future <V> { 10 V get() throws InterruptedException, ExecutionException; 11 V get(long timeout, TimeUnit unit) throws InterruptedException, 12 ExecutionException, TimeoutException; 13 14 boolean isCancelled(); 15 boolean isDone(); 16 17 boolean cancel(boolean mayInterruptIfRunning); 18 } Friday, August 2, 13
  9. where is my callback? 16 • Not able to perform

    an non-blocking operation on the result when it is ready. • Only blocking wait and busy wait are available. – future.get(10, TimeUnit.Minutes) – while (future.isDone() == false) { //do other works if they exists; otherwise Thread.sleep(1000) } Friday, August 2, 13
  10. Async callback in Java • On Android, 17 1 abstract

    class AsyncTask<Params, Progress, Result> { 2 protected Long doInBackground(Params... params) 3 protected void onProgressUpdate(Progress... progress) 4 protected void onPostExecute(Result result) 5 } 1 interface Handler.Callback { 2 boolean handleMessage (Message msg) 3 } 4 Friday, August 2, 13
  11. Async callback in Java • Guava 18 1 ListeningExecutorService service

    = MoreExecutors.listeningDecorator( 2 Executors.newFixedThreadPool(10)); 3 ListenableFuture<Explosion> explosion = service.submit(new Callable< 4 Explosion>() { 5 public Explosion call() { 6 return pushBigRedButton(); 7 } 8 }); 9 Futures.addCallback(explosion, new FutureCallback<Explosion>() { 10 // we want this handler to run immediately after we push the big red 11 button! 12 public void onSuccess(Explosion explosion) { 13 walkAwayFrom(explosion); 14 } 15 public void onFailure(Throwable thrown) { 16 battleArchNemesis(); // escaped the explosion! 17 } 18 }); Friday, August 2, 13
  12. is the callback the answer? • can it do this

    in Java? 19 public Future<Integer> plus(Future<Integer> a, Future<Integer> b) Friday, August 2, 13
  13. 1public ListenableFuture<Integer> plus( 2 final ListenableFuture<Integer> futureA, 3 final ListenableFuture<Integer>

    futureB) { 4 return Futures.transform(futureA, new AsyncFunction<Integer, Integer>() { 5 @Override 6 public ListenableFuture<Integer> apply(final Integer resultA) throws Exception { 7 return Futures.transform(futureB, new AsyncFunction<Integer, Integer>() { 8 @Override 9 public ListenableFuture<Integer> apply(Integer resultB) throws Exception { 10 return Futures.immediateCheckedFuture(resultA + resultB); 11 } 12 }); 13 } 14 }); 15} Callback Hell in Guava’s Future public interface AsyncFunction<I, O> { ListenableFuture<O> apply(I input) throws Exception; } static <I,O>ListenableFuture<O> transform( ListenableFuture<I> input, AsyncFunction<? super I,? extends O> function) one line of valuable code with 14 lines of boilerplate Friday, August 2, 13
  14. Callback Hell in Guava’s Future 21 1 ListenableFuture<List<String>> indexSearch =

    2 luceneSearcher.searchAsync(“firstName:martin”); 3 4 Function<List<String>, ListenableFuture<List<Person>>> queryFunction = 5 new Function<List<String>, ListenableFuture<List<Person>>>() { 6 @Override 7 public ListenableFuture<List<Person>> apply(final List<String> ids) { 8 return dataService.getPersonsByIdAsync(ids); 9 } 10 }; 11 12 ListenableFuture<List<Person>> results = 13 Futures.chain(indexSearch, queryFunction,executorService); Friday, August 2, 13
  15. Callback Hell in NodeJS 22 ref: http://callbackhell.com/ 1. fs.readdir(source, function(err,

    files) { 2. if (err) { 3. console.log('Error finding files: ' + err) 4. } else { 5. files.forEach(function(filename, fileIndex) { 6. console.log(filename) 7. gm(source + filename).size(function(err, values) { 8. if (err) { 9. console.log('Error identifying file size: ' + err) 10. } else { 11. console.log(filename + ' : ' + values) 12. aspect = (values.width / values.height) 13. widths.forEach(function(width, widthIndex) { 14. height = Math.round(width / aspect) 15. console.log('resizing ' + filename + 'to ' + height + 'x' + height) 16. this.resize(width, height).write(destination + 'w' + width + '_' + filename, function(err) { 17. if (err) console.log('Error writing file: ' + err) 18. }) 19. }.bind(this)) 20. } 21. }) 22. }) 23. } 24.}) Friday, August 2, 13
  16. History of Scala’s Future • scala.actors.Future • akka.dispatch.Future • scalaz.concurrent.Future

    • net.liftweb.actor.Future • com.twitter.util.Future • SIP-14 24 Friday, August 2, 13
  17. Basic ops 25 1. val resultFuture: Future[Int] = Future {

    2. 5 3. } 4. 5. • create a future and blocking wait for result. def apply[T](body: 㱺 T): Future[T] Starts an asynchronous computation and returns a Future object with the result of that computation. The result becomes available once the asynchronous computation is completed. Friday, August 2, 13
  18. Basic ops 26 1. val resultFuture = Future { 2.

    Thread.sleep(5000) 3. 5 4. } 5. // throw exception or return a value. 6. val ret = Await.result(resultFuture, 5.minutes) • create a future and blocking wait for result. def result[T](awaitable: Awaitable[T], atMost: Duration): T Await and return the result (of type T) of an Awaitable. Although this method is blocking, the internal use of blocking ensures that the underlying ExecutionContext to properly detect blocking and ensure that there are no deadlocks. Friday, August 2, 13
  19. Basic ops 27 • check the response. value: Option[Try[T]] The

    value of this Future. If the future is not completed the returned value will be None. If the future is completed the value will beSome(Success(t)) if it contains a valid result, or Some(Failure(error)) if it contains an exception. 1. resultFuture.value match { 2. case Some(Success(value)) => // do something with value 3. case Some(Failure(exception)) => // handle exception 4. case None => // do nothing 5. } Friday, August 2, 13
  20. callbacks 28 def onSuccess[U](pf: PartialFunction[T, U]): Unit When this future

    is completed successfully (i.e. with a value), apply the provided partial function to the value if the partial function is defined at that value. def onFailure[U](callback: PartialFunction[Throwable, U]): Unit When this future is completed with a failure (i.e. with a throwable), apply the provided callback to the throwable. 1. val searchFuture: Future[SearchResult] = // invoke search 2. searchFuture.onSuccess { 3. case res: SearchResult => {???}// update UI with the result. 4. } 1. searchFuture.onFailure { 2. case e: SocketTimeoutException => //notify user request timeout 3. case e: ConnectException => // notify user service is 4. // unreachable 5. } Friday, August 2, 13
  21. chaining - map 29 map[S](f: (T) 㱺 S): Future[S] Creates

    a new future by applying a function to the successful result of this future. If this future is completed with an exception then the new future will also contain this exception. 1. val historyFuture: Future[BrowseHistory] = // invoke user service 2. val future: Future[Model] = historyFuture.map { 3. history: BrowseHistory => { 4. // transform BrowseHistory to Model 5. } 6. } 1. val one = Future { 1 } 2. val two = one.map { i => i + 1 } // value is 2. Friday, August 2, 13
  22. chaining - flatMap 30 def flatMap[S](f: (T) 㱺 Future[S]): Future[S]

    Creates a new future by applying a function to the successful result of this future, and returns the result of the function as the new future. 1. val historyFuture: Future[BrowseHistory] = // invoke user service 2. val future: Future[Recommendations] = historyFuture.flatMap { 3. history: BrowseHistory => { 4. service.findRelatedItems(history) // Future[Recommendations] 5. } 6. } Friday, August 2, 13
  23. chaining - for comprehension 31 1. val result: Future[Recommendations] =

    for( 2. history <- service.getBrowseHistory(user); 3. recommendations <- service.findRelatedItems(history) 4. ) yield recommendations 1. val historyFuture: Future[BrowseHistory] = // invoke user service 2. val future: Future[Recommendations] = historyFuture.flatMap { 3. history: BrowseHistory => { 4. service.findRelatedItems(history) 5. } 6. } syntactic sugar to turn callback hell into more readable format Friday, August 2, 13
  24. composable • Future.sequence – reducing many Futures into a single

    Future. 32 1. def plusOne(n: Int): Future[Int] = future { n + 1 } 2. val items = List(1, 2, 3, 4) 3. val futures: List[Future[Int]] = items.map(n => plusOne(n)) 4. val futureOfList: Future[List[Int]] = Future.sequence(futures) Friday, August 2, 13
  25. composable 33 def zip[U](that: Future[U]): Future[(T, U)] Zips the values

    of this and that future, and creates a new future holding the tuple of their results. 1 val a = Future { 1 } 2 val b = Future { 2 } 3 val c = a zip b // return Future[(Int, Int)] Friday, August 2, 13
  26. Error Handling 34 def def fallbackTo[U >: T](that: Future[U]): Future[U]

    Creates a new future which holds the result of this future if it was completed successfully, or, if not, the result of the that future if that is completed successfully. If both futures are failed, the resulting future holds the throwable object of the first future. Using this method will not cause concurrent programs to become nondeterministic. 1 val failed = future.failed(new RuntimeException("Failed")) 2 val default = future.successful( 5 ) 3 val future = failed fallbackTo default 4 Await.result(future, Duration.Zero) // evaluates to 5 Friday, August 2, 13
  27. 35 def recover[U >: T](pf: PartialFunction[Throwable, U]): Future[U] Creates a

    new future that will handle any matching throwable that this future might contain. If there is no match, or if this future contains a valid result then the new future will contain the same. Error Handling future {6 / 0} recover { case e: ArithmeticException => 0 } // result: 0 future {6 / 2} recover { case e: ArithmeticException => 0 } // result: 3 1 future { /** network call */ } recover { 2 case e: SocketTimeoutException => ??? 3 case e: IOException => ??? 4 } Friday, August 2, 13
  28. 36 def recoverWith[U >: T](pf: PartialFunction[Throwable, Future[U]]): Future[U] Creates a

    new future that will handle any matching throwable that this future might contain by assigning it a value of another future. If there is no match, or if this future contains a valid result then the new future will contain the same result. 1. val result = Future { 2. defaultDataSource.findItem(5) //returns Future[Option[Item]] 3. } recoverWith { 4. case SocketTimeoutException => backupSource.findItem(5) 5. } Error Handling Friday, August 2, 13
  29. ExecutionContext 37 1. def apply[T](body: 㱺 T) (implicit executor: ExecutionContext):

    Future[T] 2. def map[S](f: (T) 㱺 S) (implicit executor: ExecutionContext): Future[S] 3. def flatMap[S](f: (T) 㱺 Future[S]) (implicit executor: ExecutionContext): Future[S] 4. def onSuccess[U](pf: PartialFunction[T, U]) (implicit executor: ExecutionContext): Unit 5. def onFailure[U](callback: PartialFunction[Throwable, U]) (implicit executor: ExecutionContext): Unit 6. def recover[U >: T](pf: PartialFunction[Throwable, U]) (implicit executor: ExecutionContext): Future[U] 7. def recoverWith[U >: T](pf: PartialFunction[Throwable, Future[U]]) (implicit executor: ExecutionContext): Future[U] Friday, August 2, 13
  30. ExecutionContext • where future get executed. – default: ForkJoinPool –

    custom: java5 ExecutorService 38 1. implicit val executionContext = 2. ExecutionContext.fromExecutorService( 3. Executors.newFixedThreadPool(5) 4. ) import scala.concurrent.ExecutionContext.Implicits.global Friday, August 2, 13
  31. DEMO • A None-blocking parallel external merge sort – sort

    numbers in files. – files come from s3. Their size can be ranged from 100mb to 10gb. – I don’t want to fully read the file and then sort the file. 40 Friday, August 2, 13
  32. 41 Stream of Int arrays Stream of sorted data merged

    Stream of Ints Friday, August 2, 13