• Save
Functional Reactive Programming in the Netflix API
Upcoming SlideShare
Loading in...5
×
 

Functional Reactive Programming in the Netflix API

on

  • 3,808 views

Video and slides synchronized, mp3 and slide download available at http://bit.ly/XRdkqc. ...

Video and slides synchronized, mp3 and slide download available at http://bit.ly/XRdkqc.

Ben Christensen describes how Neflix has optimized their API using a functional reactive programming (modeled after Rx) in a polyglot Java stack. Filmed at qconlondon.com.

Ben Christensen is a software engineer on the Netflix API Platform team responsible for fault tolerance, performance, architecture and scale while enabling millions of customers to access the Netflix experience across more than 800 different device types. Prior to Netflix, Ben was at Apple in the iTunes division making iOS apps and media available to the world. Twitter: @benjchristensen

Statistics

Views

Total Views
3,808
Views on SlideShare
3,780
Embed Views
28

Actions

Likes
16
Downloads
0
Comments
0

1 Embed 28

https://twitter.com 28

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

    Functional Reactive Programming in the Netflix API Functional Reactive Programming in the Netflix API Presentation Transcript

    • Functional Reactive Programmingin the Netflix APIBen ChristensenSoftware Engineer – API Platform at Netflix@benjchristensenhttp://www.linkedin.com/in/benjchristensenhttp://techblog.netflix.com/QCon London – March 6 2013onsdag den 6. marts 13
    • InfoQ.com: News & Community Site• 750,000 unique visitors/month• Published in 4 languages (English, Chinese, Japanese and BrazilianPortuguese)• Post content from our QCon conferences• News 15-20 / week• Articles 3-4 / week• Presentations (videos) 12-15 / week• Interviews 2-3 / week• Books 1 / monthWatch the video with slidesynchronization on InfoQ.com!http://www.infoq.com/presentations/netflix-functional-rx
    • Presented at QCon Londonwww.qconlondon.comPurpose of QCon- to empower software development by facilitating the spread ofknowledge and innovationStrategy- practitioner-driven conference designed for YOU: influencers ofchange and innovation in your teams- speakers and topics driving the evolution and innovation- connecting and catalyzing the influencers and innovatorsHighlights- attended by more than 12,000 delegates since 2007- held in 9 cities worldwide
    • onsdag den 6. marts 13
    • More than 33 million Subscribersin more than 50 Countries and Territoriesonsdag den 6. marts 13
    • Netflix accounts for 33% of Peak DownstreamInternet Traffic in North AmericaNetflix subscribers are watchingmore than 1 billion hours a monthonsdag den 6. marts 13
    • API traffic has grown from~20 million/day in 2010 to >2 billion/day05001000150020002010 2011 2012 TodaymillionsofAPIrequestsperdayonsdag den 6. marts 13
    • Discovery Streamingonsdag den 6. marts 13
    • Netflix API Streamingonsdag den 6. marts 13
    • onsdag den 6. marts 13
    • Open API Netflix DevicesAPI Request Volume by Audienceonsdag den 6. marts 13
    • Netflix APIDependency ADependency DDependency GDependency JDependency MDependency PDependency BDependency EDependency HDependency KDependency NDependency QDependency CDependency FDependency IDependency LDependency ODependency Ronsdag den 6. marts 13
    • /ps3/homeDependency F10 ThreadsDependency G10 ThreadsDependency H10 ThreadsDependency I5 ThreadsDependency J8 ThreadsDependency A10 ThreadsDependency B8 ThreadsDependency C10 ThreadsDependency D15 ThreadsDependency E5 ThreadsDependency K15 ThreadsDependency L4 ThreadsDependency M5 ThreadsDependency N10 ThreadsDependency O10 ThreadsDependency P10 ThreadsDependency Q8 ThreadsDependency R10 ThreadsDependency S8 ThreadsDependency T10 Threads/android/home/tv/homeFunctional Reactive Dynamic EndpointsAsynchronous Java APIonsdag den 6. marts 13
    • /ps3/homeDependency F10 ThreadsDependency G10 ThreadsDependency H10 ThreadsDependency I5 ThreadsDependency J8 ThreadsDependency A10 ThreadsDependency B8 ThreadsDependency C10 ThreadsDependency D15 ThreadsDependency E5 ThreadsDependency K15 ThreadsDependency L4 ThreadsDependency M5 ThreadsDependency N10 ThreadsDependency O10 ThreadsDependency P10 ThreadsDependency Q8 ThreadsDependency R10 ThreadsDependency S8 ThreadsDependency T10 Threads/android/home/tv/homeFunctional Reactive Dynamic EndpointsAsynchronous Java APIHystrixfault-isolation layeronsdag den 6. marts 13
    • /ps3/homeDependency F10 ThreadsDependency G10 ThreadsDependency H10 ThreadsDependency I5 ThreadsDependency J8 ThreadsDependency A10 ThreadsDependency B8 ThreadsDependency C10 ThreadsDependency D15 ThreadsDependency E5 ThreadsDependency K15 ThreadsDependency L4 ThreadsDependency M5 ThreadsDependency N10 ThreadsDependency O10 ThreadsDependency P10 ThreadsDependency Q8 ThreadsDependency R10 ThreadsDependency S8 ThreadsDependency T10 Threads/android/home/tv/homeFunctional Reactive Dynamic EndpointsAsynchronous Java APIonsdag den 6. marts 13
    • RxJava“a library for composingasynchronous and event-basedprograms using observablesequences for the Java VM”A Java port of Rx (Reactive Extensions)https://rx.codeplex.com (.Net and Javascript by Microsoft)onsdag den 6. marts 13
    • Do we really need another way of“managing” concurrency?onsdag den 6. marts 13
    • Discovery of Rx began with a re-architecture ...onsdag den 6. marts 13
    • ... that collapsed network traffic into coarse API calls ...onsdag den 6. marts 13
    • ... that collapsed network traffic into coarse API calls ...Nested, conditional, parallel executiononsdag den 6. marts 13
    • ... and wewanted to allowanybody tocreate endpoints,not just the“API Team”onsdag den 6. marts 13
    • onsdag den 6. marts 13
    • Concurrency withouteach engineer readingand re-reading this ->(awesome book ... everybody isn’tgoing to - or should have to - readit though, that’s the point)onsdag den 6. marts 13
    • Owner of API should retaincontrol of concurrency behavior.onsdag den 6. marts 13
    • public Data getData();What if the implementation needs to changefrom synchronous to asynchronous?How should the client execute that methodwithout blocking? spawn a thread?Owner of API should retaincontrol of concurrency behavior.onsdag den 6. marts 13
    • public void getData(Callback<T> c);public Future<T> getData();public Future<List<Future<T>>> getData();What about ... ?onsdag den 6. marts 13
    • IterablepullObservablepushT next()throws Exceptionreturns;onNext(T)onError(Exception)onCompleted()onsdag den 6. marts 13
    • IterablepullObservablepushT next()throws Exceptionreturns;onNext(T)onError(Exception)onCompleted()  //  Iterable<String>    //  that  contains  75  Strings  getDataFromLocalMemory()    .skip(10)    .take(5)    .map({  s  -­‐>        return  s  +  "_transformed"})    .forEach(          {  println  "next  =>  "  +  it})  //  Observable<String>    //  that  emits  75  Strings  getDataFromNetwork()    .skip(10)    .take(5)    .map({  s  -­‐>        return  s  +  "_transformed"})    .subscribe(          {  println  "onNext  =>  "  +  it})onsdag den 6. marts 13
    • IterablepullObservablepushT next()throws Exceptionreturns;onNext(T)onError(Exception)onCompleted()  //  Iterable<String>    //  that  contains  75  Strings  getDataFromLocalMemory()    .skip(10)    .take(5)    .map({  s  -­‐>        return  s  +  "_transformed"})    .forEach(          {  println  "onNext  =>  "  +  it})  //  Observable<String>    //  that  emits  75  Strings  getDataFromNetwork()    .skip(10)    .take(5)    .map({  s  -­‐>        return  s  +  "_transformed"})    .subscribe(          {  println  "onNext  =>  "  +  it})onsdag den 6. marts 13
    • Instead of blocking APIs ...class  VideoService  {      def  VideoList  getPersonalizedListOfMovies(userId);      def  VideoBookmark  getBookmark(userId,  videoId);      def  VideoRating  getRating(userId,  videoId);      def  VideoMetadata  getMetadata(videoId);}class  VideoService  {      def  Observable<VideoList>  getPersonalizedListOfMovies(userId);      def  Observable<VideoBookmark>  getBookmark(userId,  videoId);      def  Observable<VideoRating>  getRating(userId,  videoId);      def  Observable<VideoMetadata>  getMetadata(videoId);}... create Observable APIs:onsdag den 6. marts 13
    • onsdag den 6. marts 13
    • onsdag den 6. marts 13
    • onsdag den 6. marts 13
    • onsdag den 6. marts 13
    • onsdag den 6. marts 13
    • onsdag den 6. marts 13
    • Observable.toObservable("one",  "two",  "three")          .take(2)          .subscribe((arg)  -­‐>  {                    System.out.println(arg);          });Java8Observable.toObservable("one",  "two",  "three")    .take(2)    .subscribe((arg:  String)  =>  {            println(arg)    })Scala(-­‐>      (Observable/toObservable  ["one"  "two"  "three"])    (.take  2)      (.subscribe  (fn  [arg]  (println  arg))))Clojure    Observable.toObservable("one",  "two",  "three")        .take(2)          .subscribe({arg  -­‐>  println(arg)})Groovy    Observable.toObservable("one",  "two",  "three")        .take(2)          .subscribe(lambda  {  |arg|  puts  arg  })JRubyonsdag den 6. marts 13
    •        Observable.create({  observer  -­‐>            try  {                  observer.onNext(new  Video(id))                observer.onCompleted();            }  catch(Exception  e)  {                observer.onError(e);            }        })onsdag den 6. marts 13
    •        def  Observable<VideoRating>  getRating(userId,  videoId)  {                //  fetch  the  VideoRating  for  this  user  asynchronously                return  Observable.create({  observer  -­‐>                        executor.execute(new  Runnable()  {                                def  void  run()  {                                    try  {                                          VideoRating  rating  =  ...  do  network  call  ...                                        observer.onNext(rating)                                        observer.onCompleted();                                      }  catch(Exception  e)  {                                        observer.onError(e);                                      }                                      }                        })                })        }Asynchronous Observable with Single Valueonsdag den 6. marts 13
    •        def  Observable<VideoRating>  getRating(userId,  videoId)  {                //  fetch  the  VideoRating  for  this  user  asynchronously                return  Observable.create({  observer  -­‐>                        executor.execute(new  Runnable()  {                                def  void  run()  {                                    try  {                                          VideoRating  rating  =  ...  do  network  call  ...                                        observer.onNext(rating)                                        observer.onCompleted();                                      }  catch(Exception  e)  {                                        observer.onError(e);                                      }                                      }                        })                })        }Asynchronous Observable with Single Valueonsdag den 6. marts 13
    • Synchronous Observable with Multiple Values        def  Observable<Video>  getVideos()  {                return  Observable.create({  observer  -­‐>                      try  {                              for(v  in  videos)  {                                observer.onNext(v)                          }                          observer.onCompleted();                      }  catch(Exception  e)  {                          observer.onError(e);                      }                })        }Caution: This is eager and will always emit all valuesregardless of subsequent operators such as take(10)onsdag den 6. marts 13
    • Synchronous Observable with Multiple Values        def  Observable<Video>  getVideos()  {                return  Observable.create({  observer  -­‐>                      try  {                              for(v  in  videos)  {                                observer.onNext(v)                          }                          observer.onCompleted();                      }  catch(Exception  e)  {                          observer.onError(e);                      }                })        }Caution: This is eager and will always emit all valuesregardless of subsequent operators such as take(10)onsdag den 6. marts 13
    • Asynchronous Observable with Multiple Values  def  Observable<Video>  getVideos()  {        return  Observable.create({  observer  -­‐>              executor.execute(new  Runnable()  {                    def  void  run()  {                        try  {                                for(id  in  videoIds)  {                                  Video  v  =  ...  do  network  call  ...                                  observer.onNext(v)                              }                              observer.onCompleted();                          }  catch(Exception  e)  {                              observer.onError(e);                          }                      }              })        })  }onsdag den 6. marts 13
    • Asynchronous Observable with Multiple Values  def  Observable<Video>  getVideos()  {        return  Observable.create({  observer  -­‐>              executor.execute(new  Runnable()  {                    def  void  run()  {                        try  {                                for(id  in  videoIds)  {                                  Video  v  =  ...  do  network  call  ...                                  observer.onNext(v)                              }                              observer.onCompleted();                          }  catch(Exception  e)  {                              observer.onError(e);                          }                      }              })        })  }onsdag den 6. marts 13
    • Observable<SomeData> a = getDataA();Observable<SomeData> b = getDataB();Observable<SomeData> c = getDataC();Observable.merge(a, b, c).subscribe({ element -> println("data: " + element)},{ exception -> println("error occurred: "+ exception.getMessage())})Combining via Mergeonsdag den 6. marts 13
    • Observable<SomeData> a = getDataA();Observable<String> b = getDataB();Observable<MoreData> c = getDataC();Observable.zip(a, b, c, {x, y, z -> [x, y, z]}).subscribe({ triple -> println("a: " + triple[0]+ " b: " + triple[1]+ " c: " + triple[2])},{ exception -> println("error occurred: "+ exception.getMessage())})Combining via Ziponsdag den 6. marts 13
    • Observable<SomeData> a = getDataA();Observable<String> b = getDataB();Observable<MoreData> c = getDataC();Observable.zip(a, b, c, {x, y, z -> [x, y, z]}).subscribe({ triple -> println("a: " + triple[0]+ " b: " + triple[1]+ " c: " + triple[2])},{ exception -> println("error occurred: "+ exception.getMessage())})Error Handlingonsdag den 6. marts 13
    • Observable<SomeData> a = getDataA();Observable<String> b = getDataB();Observable<MoreData> c = getDataC().onErrorResumeNext(getFallbackForDataC());Observable.zip(a, b, c, {x, y, z -> [x, y, z]}).subscribe({ triple -> println("a: " + triple[0]+ " b: " + triple[1]+ " c: " + triple[2])},{ exception -> println("error occurred: "+ exception.getMessage())})Error Handlingonsdag den 6. marts 13
    • def Observable getVideos(userId) {return VideoService.getVideos(userId)}onsdag den 6. marts 13
    • def Observable getVideos(userId) {return VideoService.getVideos(userId)}Asynchronous request thatreturns Observable<Video>onsdag den 6. marts 13
    • def Observable<Map> getVideos(userId) {return VideoService.getVideos(userId)// we only want the first 10 of each list.take(10)}onsdag den 6. marts 13
    • def Observable<Map> getVideos(userId) {return VideoService.getVideos(userId)// we only want the first 10 of each list.take(10)}Reactive operator on the Observablethat takes the first 10 Video objectsthen unsubscribes.onsdag den 6. marts 13
    • def Observable<Map> getVideos(userId) {return VideoService.getVideos(userId)// we only want the first 10 of each list.take(10).map({ Video video ->// transform video object})}onsdag den 6. marts 13
    • def Observable<Map> getVideos(userId) {return VideoService.getVideos(userId)// we only want the first 10 of each list.take(10).map({ Video video ->// transform video object})}The ‘map’ operator allows transformingthe input value into a different output.onsdag den 6. marts 13
    •        Observable<R>  b  =  Observable<T>.map({  T  t  -­‐>              R  r  =  ...  transform  t  ...            return  r;        })onsdag den 6. marts 13
    • def Observable<Map> getVideos(userId) {return VideoService.getVideos(userId)// we only want the first 10 of each list.take(10).mapMany({ Video video ->// for each video we want to fetch metadatadef m = video.getMetadata().map({ Map<String, String> md ->// transform to the data and format we wantreturn [title: md.get("title"),length: md.get("duration")]})// and its rating and bookmarkdef b ...def r ...})}onsdag den 6. marts 13
    • def Observable<Map> getVideos(userId) {return VideoService.getVideos(userId)// we only want the first 10 of each list.take(10).mapMany({ Video video ->// for each video we want to fetch metadatadef m = video.getMetadata().map({ Map<String, String> md ->// transform to the data and format we wantreturn [title: md.get("title"),length: md.get("duration")]})// and its rating and bookmarkdef b ...def r ...})}We change to ‘mapMany’ which islike merge(map()) since we will returnan Observable<T> instead of T.onsdag den 6. marts 13
    •  Observable<R>  b  =  Observable<T>.mapMany({  T  t  -­‐>          Observable<R>  r  =  ...  transform  t  ...        return  r;  })onsdag den 6. marts 13
    •  Observable<R>  b  =  Observable<T>.mapMany({  T  t  -­‐>          Observable<R>  r  =  ...  transform  t  ...        return  r;  })onsdag den 6. marts 13
    • def Observable<Map> getVideos(userId) {return VideoService.getVideos(userId)// we only want the first 10 of each list.take(10).mapMany({ Video video ->// for each video we want to fetch metadatadef m = video.getMetadata().map({ Map<String, String> md ->// transform to the data and format we wantreturn [title: md.get("title"),length: md.get("duration")]})// and its rating and bookmarkdef b ...def r ...})}Nested asynchronous callsthat return more Observables.onsdag den 6. marts 13
    • def Observable<Map> getVideos(userId) {return VideoService.getVideos(userId)// we only want the first 10 of each list.take(10).mapMany({ Video video ->// for each video we want to fetch metadatadef m = video.getMetadata().map({ Map<String, String> md ->// transform to the data and format we wantreturn [title: md.get("title"),length: md.get("duration")]})// and its rating and bookmarkdef b ...def r ...})}Observable<VideoMetadata>Observable<VideoBookmark>Observable<VideoRating>onsdag den 6. marts 13
    • def Observable<Map> getVideos(userId) {return VideoService.getVideos(userId)// we only want the first 10 of each list.take(10).mapMany({ Video video ->// for each video we want to fetch metadatadef m = video.getMetadata().map({ Map<String, String> md ->// transform to the data and format we wantreturn [title: md.get("title"),length: md.get("duration")]})// and its rating and bookmarkdef b ...def r ...})}Each Observable transformsits data using ‘map’onsdag den 6. marts 13
    • def Observable<Map> getVideos(userId) {return VideoService.getVideos(userId)// we only want the first 10 of each list.take(10).mapMany({ Video video ->// for each video we want to fetch metadatadef m = video.getMetadata().map({ Map<String, String> md ->// transform to the data and format we wantreturn [title: md.get("title"),length: md.get("duration")]})// and its rating and bookmarkdef b ...def r ...// compose these together})}onsdag den 6. marts 13
    • def Observable<Map> getVideos(userId) {return VideoService.getVideos(userId)// we only want the first 10 of each list.take(10).mapMany({ Video video ->def m ...def b ...def r ...// compose these together})}onsdag den 6. marts 13
    • def Observable<Map> getVideos(userId) {return VideoService.getVideos(userId)// we only want the first 10 of each list.take(10).mapMany({ Video video ->def m ...def b ...def r ...// compose these togetherreturn Observable.zip(m, b, r, {metadata, bookmark, rating ->// now transform to complete dictionary// of data we want for each Videoreturn [id: video.videoId]<< metadata << bookmark << rating})})}onsdag den 6. marts 13
    • def Observable<Map> getVideos(userId) {return VideoService.getVideos(userId)// we only want the first 10 of each list.take(10).mapMany({ Video video ->def m ...def b ...def r ...// compose these togetherreturn Observable.zip(m, b, r, {metadata, bookmark, rating ->// now transform to complete dictionary// of data we want for each Videoreturn [id: video.videoId]<< metadata << bookmark << rating})})}The ‘zip’ operator combines the 3asynchronous Observables into 1onsdag den 6. marts 13
    •        Observable.zip(a,  b,  {  a,  b,  -­‐>              ...  operate  on  values  from  both  a  &  b  ...            return  [a,  b];  //  i.e.  return  tuple        })onsdag den 6. marts 13
    • def Observable<Map> getVideos(userId) {return VideoService.getVideos(userId)// we only want the first 10 of each list.take(10).mapMany({ Video video ->def m ...def b ...def r ...// compose these togetherreturn Observable.zip(m, b, r, {metadata, bookmark, rating ->// now transform to complete dictionary// of data we want for each Videoreturn [id: video.videoId]<< metadata << bookmark << rating})})}return a single Map (dictionary)of transformed and combined datafrom 4 asynchronous callsonsdag den 6. marts 13
    • onsdag den 6. marts 13
    • Observable<Video> emits n videos to onNext()onsdag den 6. marts 13
    • Takes first 10 then unsubscribes from origin.Returns Observable<Video> that emits 10 Videos.onsdag den 6. marts 13
    • For each of the 10 Video objects it transformsvia ‘mapMany’ function that does nested async calls.onsdag den 6. marts 13
    • For each Video ‘v’ it calls getMetadata()which returns Observable<VideoMetadata>These nested asyncrequests return Observablesthat emit 1 value.onsdag den 6. marts 13
    • The Observable<VideoMetadata> is transformed viaa ‘map’ function to return a Map of key/values.onsdag den 6. marts 13
    • Same for Observable<VideoBookmark> andObservable<VideoRating>onsdag den 6. marts 13
    • The 3 ‘mapped’ Observables are combined with a‘zip’ function that emits a Map with all data.onsdag den 6. marts 13
    • The full sequence emits Observable<Map> thatemits a Map for each of 10 Videos.onsdag den 6. marts 13
    • Client code treats all interactionswith the API as asynchronousThe API implementation chooseswhether something isblocking or non-blockingandwhat resources it uses.onsdag den 6. marts 13
    • Example of latency reduction achieved by increasingnumber of threads used by Observables.onsdag den 6. marts 13
    • +Observable<User>  u  =  new  GetUserCommand(id).observe();Observable<Geo>  g  =  new  GetGeoCommand(request).observe();Observable.zip(u,  g,  {user,  geo  -­‐>                  return  [username:  user.getUsername(),                                  currentLocation:  geo.getCounty()]      })RxJava coming to Hystrixhttps://github.com/Netflix/Hystrixonsdag den 6. marts 13
    • <dependency  org="com.netflix.rxjava"  name="rxjava-­‐core"  rev="x.y.z"  /><dependency  org="com.netflix.rxjava"  name="rxjava-­‐groovy"  rev="x.y.z"  /><dependency  org="com.netflix.rxjava"  name="rxjava-­‐clojure"  rev="x.y.z"  /><dependency  org="com.netflix.rxjava"  name="rxjava-­‐scala"  rev="x.y.z"  /><dependency  org="com.netflix.rxjava"  name="rxjava-­‐jruby"  rev="x.y.z"  /><dependency>        <groupId>com.netflix.rxjava</groupId>        <artifactId>rxjava-­‐core</artifactId>        <version>x.y.z</version></dependency>... or for a different language ...To get started ...onsdag den 6. marts 13
    • Functional Reactive in the Netflix API with RxJavahttp://techblog.netflix.com/2013/02/rxjava-netflix-api.htmlOptimizing the Netflix APIhttp://techblog.netflix.com/2013/01/optimizing-netflix-api.htmlRxJavahttps://github.com/Netflix/RxJava@RxJavaBen Christensen@benjchristensenhttp://www.linkedin.com/in/benjchristensenNetflix is Hiringhttp://jobs.netflix.comonsdag den 6. marts 13