The Play Framework at LinkedIn: productivity and performance at scale - Jim Brikman

  • 342 views
Uploaded on

At LinkedIn, we have started to use the Play Framework to build front-end and back-end services at massive scale. Play does things a little differently: it's a Java and Scala web framework, but it …

At LinkedIn, we have started to use the Play Framework to build front-end and back-end services at massive scale. Play does things a little differently: it's a Java and Scala web framework, but it doesn't follow the servlet spec; it's fairly new, but it runs on top of robust technologies like Akka and Netty; it uses a thread pool, but it's built for non-blocking I/O and reactive programming; most importantly, it's high performance, but also high productivity. We've found that the Play Framework is one of the few frameworks that is able to maintain the delicate balance of performance, reliability, and developer productivity. In the Java and Scala world, nothing even comes close. In this talk, I'll share what we've learned so far, including details of rapid iteration with Java and Scala, the story behind async I/O on the JVM, support for real time web apps (comet, WebSockets), and integrating Play into a large existing codebase.

More in: Technology , Education
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to comment
No Downloads

Views

Total Views
342
On Slideshare
0
From Embeds
0
Number of Embeds
2

Actions

Shares
Downloads
0
Comments
0
Likes
2

Embeds 0

No embeds

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide

Transcript

  • 1. atProductivity and Performance at Scale
  • 2. You are a developer using Java tobuild web services.
  • 3. This is your life:
  • 4. Waiting. [1]
  • 5. tail -f logs/catalina.out
  • 6. XML soup
  • 7. "Convenient proxy factory bean superclass for proxyfactory beans that create only singletons." [2]
  • 8. Thread pool usageLatencyThread pool hell
  • 9. There is a better way.
  • 10. Ruby on Rails!
  • 11. Nah, just kidding. We have real work to do.
  • 12. A modern web framework for Java and Scala
  • 13. The world’s largest professional network
  • 14. atWeve been using Play in production formore than 6 months [3]
  • 15. A few apps built on Play
  • 16. Channels (frontend)
  • 17. Premium Subscriptions (frontend)
  • 18. Polls (frontend + backend)
  • 19. REST search (internal tool)
  • 20. About meLeading the Play project as part of LinkedIns Service Infrastructure Team.Also: hackdays, engineering blog, incubator, open source.
  • 21. This talk is the story of building web servicesat massive scale with Java and Scala...
  • 22. ... while maintaining performance,reliability, and developerproductivity.
  • 23. Outline1. Getting started with Play2. Make a change and reload3. Error handling4. Threaded vs. evented5. Non-blocking I/O6. Scala7. Performance8. Community
  • 24. Outline1. Getting started with Play2. Make a change and reload3. Error handling4. Threaded vs. evented5. Non-blocking I/O6. Scala7. Performance8. Community
  • 25. Download and install Play fromhttp://www.playframework.com
  • 26. > play new my-app
  • 27. > play idea> play eclipse
  • 28. > play run
  • 29. http://localhost:9000
  • 30. Application layoutapp → Application sources└ assets → Compiled asset sources└ controllers → Application controllers└ models → Application business layer└ views → Templatesconf → Configurations files└ application.conf → Main configuration file└ routes → Routes definitionpublic → Public assets└ stylesheets → CSS files└ javascripts → Javascript files└ images → Image filesproject → sbt configuration files└ Build.scala → Application build script└ plugins.sbt → sbt pluginslib → Unmanaged librariesdependencieslogs → Standard logs foldertarget → Generated stufftest → Unit or functional tests
  • 31. Outline1. Getting started with Play2. Make a change and reload3. Error handling4. Threaded vs. evented5. Non-blocking I/O6. Scala7. Performance8. Community
  • 32. public class HelloWorld extends Controller {public static Result index() {return ok("Hello World");}}Create a new controller and actionapp/controllers/HelloWorld.java
  • 33. Dont worry about the use of static. Yes,Play supports IOC. Using static (and othershortcuts) lets me keep the examples simple.
  • 34. GET /hello controllers.HelloWorld.index()Expose the controller/action at a URLconf/routes
  • 35. Now, restart the server.
  • 36. Nah, just kidding. Refresh the page.
  • 37. Woohoo, hot reload!
  • 38. http://localhost:9000/hello
  • 39. public class HelloWorld extends Controller {public static Result index(String name) {return ok("Hello " + name);}}Add a parameterapp/controllers/HelloWorld.java
  • 40. GET /hello controllers.HelloWorld.index( name)Read the parameter from the query stringconf/routes
  • 41. http://localhost:9000/hello?name=Jim
  • 42. GET /hello/:name controllers.HelloWorld.index(name)Read the parameter from the URL insteadconf/routes
  • 43. http://localhost:9000/hello/Jim
  • 44. public class HelloWorld extends Controller {public static Result index(String name, int age) {return ok("Hello " + name + " you are " + age +" years old");}}Add another parameter, this time an intapp/controllers/HelloWorld.java
  • 45. GET /hello/:name/ :age controllers.HelloWorld.index(name: String, age: Int)Add the parameter. Note the type checking!conf/routes
  • 46. http://localhost:9000/hello/Jim/28
  • 47. @(name: String, age: Int)<html><head></head><body><img src="/assets/images/play-logo.png"/><p>Hello <b>@name</b>, you are <b>@age</b> years old</p></body></html>Add a viewapp/views/hello.scala.html
  • 48. public class HelloWorld extends Controller {public static Result index(String name, int age) {return ok(views.html.hello.render(name, age));}}Render the view from the controllerapp/controllers/HelloWorld.java
  • 49. http://localhost:9000/hello/Jim/28
  • 50. location = "JaxConf"How about some config?app/conf/application.conf
  • 51. public class HelloWorld extends Controller {public static Result index(String name, int age) {String location = getConfig().getString("location");return ok(views.html.hello.render(name, age,location));}private static Configuration getConfig() {return Play.application().configuration();}}Read the config and pass it to the viewapp/controllers/HelloWorld.java
  • 52. @(name: String, age: Int, location: String)<html><head></head><body><img src="/assets/images/play-logo.png"/><p>Hello <b>@name</b>, you are <b>@age</b> years oldand you are at <b>@location</b></p></body></html>Update the viewapp/views/hello.scala.html
  • 53. Refresh the page. Config hot reloads too!
  • 54. Outline1. Getting started with Play2. Make a change and reload3. Error handling4. Threaded vs. evented5. Non-blocking I/O6. Scala7. Performance8. Community
  • 55. In dev mode, Play shows error messagesright in the browser.
  • 56. Parameter type checking
  • 57. A helpful 404 page
  • 58. Compile errors show problematic source codein the browser
  • 59. Views are compiled as well
  • 60. The routes file too
  • 61. Outline1. Getting started with Play2. Make a change and reload3. Error handling4. Threaded vs. evented5. Non-blocking I/O6. Scala7. Performance8. Community
  • 62. Most people are used to threaded servers
  • 63. Threaded servers assign one thread perrequest and use blocking I/Ovoid doGet(HttpServletRequest req, HttpServletResponse res) {// Apache HttpClientHttpClient client = new HttpClient();GetMethod method = new GetMethod("www.example.com/");// executeMethod is a blocking, synchronous callint statusCode = client.executeMethod(method);System.out.println("Response " + statusCode);}MyServlet.java
  • 64. Evented servers are gaining popularity
  • 65. Evented servers have one thread/process perCPU core and use non-blocking I/OMyNodeApp.jsvar callback = function(data) {console.log("Response: " + data);};var options = {hostname: www.google.com,path: /upload};// Non-blocking HTTP callhttp.request(options, callback);console.log(This line may execute before the callback!);
  • 66. Why threaded vs. evented matters forLinkedIn
  • 67. LinkedIn uses a Service Oriented ArchitectureInternet LoadBalancerFrontendServerFrontendServerFrontendServerBackendServerBackendServerBackendServerBackendServerBackendServerDataStoreDataStoreDataStoreDataStore
  • 68. void doGet(HttpServletRequest req, HttpServletResponse res) {// Call a number of backend services to get dataProfile profile = profileSvc.getProfile();Company company = companySvc.getCompany();Skills skills = skillsSvc.getSkills();}MyServlet.javaOur services spend most of their time waitingfor data from other services and data stores
  • 69. I/O is very expensive [4]
  • 70. In a threaded server, threads spend most ofthe time idle, waiting on I/O
  • 71. Threading dilemma1. Creating new threads on the fly is expensive:a. Use a thread pool2. Too many threads in the thread pool:a. Memory overheadb. Context switching overhead3. Too few threads in the thread pool:a. Run out of threads, latency goes upb. Sensitive to downstream latency!
  • 72. Internet LoadBalancerFrontendServerFrontendServerFrontendServerBackendServerBackendServerBackendServerBackendServerBackendServerDataStoreDataStoreDataStoreDataStoreLets say latency goesup a little here
  • 73. Internet LoadBalancerFrontendServerFrontendServerFrontendServerBackendServerBackendServerBackendServerBackendServerBackendServerDataStoreDataStoreDataStoreDataStoreCauses threads to getbacked up here
  • 74. Internet LoadBalancerFrontendServerFrontendServerFrontendServerBackendServerBackendServerBackendServerBackendServerBackendServerDataStoreDataStoreDataStoreDataStoreLatency goes up
  • 75. Internet LoadBalancerFrontendServerFrontendServerFrontendServerBackendServerBackendServerBackendServerBackendServerBackendServerDataStoreDataStoreDataStoreDataStoreNow threads getbacked up here
  • 76. Internet LoadBalancerFrontendServerFrontendServerFrontendServerBackendServerBackendServerBackendServerBackendServerBackendServerDataStoreDataStoreDataStoreDataStoreAnd here
  • 77. Internet LoadBalancerFrontendServerFrontendServerFrontendServerBackendServerBackendServerBackendServerBackendServerBackendServerDataStoreDataStoreDataStoreDataStoreHere too
  • 78. Internet LoadBalancerFrontendServerFrontendServerFrontendServerBackendServerBackendServerBackendServerBackendServerBackendServerDataStoreDataStoreDataStoreDataStoreAnd there
  • 79. Internet LoadBalancerFrontendServerFrontendServerFrontendServerBackendServerBackendServerBackendServerBackendServerBackendServerDataStoreDataStoreDataStoreDataStoreAnd... the site is down.
  • 80. This is thread pool hell [5]
  • 81. Play is built on top of Netty, so it supports non-blocking I/O [6]
  • 82. NIO benefits1. No sensitivity to downstream slowness2. Easy to parallelize I/O3. Supports many concurrent and long-runningconnections, enabling:a. WebSocketsb. Cometc. Server-Sent Events
  • 83. Outline1. Getting started with Play2. Make a change and reload3. Error handling4. Threaded vs. evented5. Non-blocking I/O6. Scala7. Performance8. Community
  • 84. public class Proxy extends Controller {public static Result index(String url) {// Non blocking HTTP callPromise<Response> responsePromise = WS.url(url).get();// How do we turn a Promise into a Play Result?}}app/controllers/Proxy.javaUse Plays built in WS library to make a non-blocking HTTP call
  • 85. A Promise<T> will eventually contain thevalue T (or an error)
  • 86. (Play Framework source code)Play has a built-in subclass of Result calledAsyncResult that takes a Promise<Result>public static class AsyncResult implements Result {private final Promise<Result> promise;public AsyncResult(Promise<Result> promise) {this.promise = promise;}}
  • 87. public class Proxy extends Controller {public static Result index(String url) {// Non blocking HTTP callPromise<Response> response = WS.url(url).get();// Transform asynchronously into a Play ResultPromise<Result> result = response.map(toResult);return async(result);}// A function that can transform a Response into a Resultprivate static Function<Response, Result> toResult =new Function<Response, Result>() {public Result apply(Response response) {return ok(response.getBody()).as("text/html");}};}app/controllers/Proxy.javaWe can use the map method to turn aPromise<Response> into a Promise<Result>
  • 88. GET /proxy controllers.Proxy.index(url)Lets add this endpoint to the routes fileconf/routes
  • 89. http://localhost:9000/proxy?url=http://example.com
  • 90. We just built a completelynon-blocking proxy!
  • 91. public class Proxy extends Controller {public static Result index(String url) {Logger.info("Before the HTTP call");Promise<Response> response = WS.url(url).get();Promise<Result> result = response.map(toResult);Logger.info("After the HTTP call");return async(result);}private static Function<Response, Result> toResult =new Function<Response, Result>() {public Result apply(Response response) {Logger.info("Inside the toResult function");return ok(response.getBody()).as("text/html");}};}app/controllers/Proxy.javaTo see that its non-blocking, lets add logging
  • 92. Refresh the page and the logs show theHTTP call really is non-blocking
  • 93. NIO makes parallel requests easy.Lets try it.
  • 94. class Timing {public String url;public long latency;public Timing(String url, long latency) {this.url = url;this.latency = latency;}// Fire an HTTP request and record how long it tookpublic static Promise<Timing> timedRequest(final String url) {final long start = System.currentTimeMillis();Promise<Response> res = WS.url(url).get();return res.map(new Function<Response, Timing>() {public Timing apply(Response response) {long latency = System.currentTimeMillis() - start;return new Timing(url, latency);}});}}app/models/Timing.javaFirst, define a class that times an HTTP request
  • 95. public class Parallel extends Controller {public static Result index() {// A Promise that will redeem when the 3 parallel// HTTP requests are donePromise<List<Timing>> all = Promise.waitAll(Timing.timedRequest("http://www.yahoo.com"),Timing.timedRequest("http://www.google.com"),Timing.timedRequest("http://www.bing.com"));// Asynchronously transform timings into a JSON responsereturn async(all.map(new Function<List<Timing>, Result>() {public Result apply(List<Timing> timings) {return ok(Json.toJson(timings));}}));}}app/controllers/Parallel.javaNext, add a controller that fetches 3 URLs inparallel and returns the timings as JSON
  • 96. GET /parallel controllers.Parallel.index()Add it to the routes fileconf/routes
  • 97. http://localhost:9000/parallel
  • 98. Outline1. Getting started with Play2. Make a change and reload3. Error handling4. Threaded vs. evented5. Non-blocking I/O6. Scala7. Performance8. Community
  • 99. Play has native support for Scala
  • 100. app/controllers/HelloWorldScala.scalaJust add a .scala file under /app andPlay will compile itobject HelloWorldScala extends Controller {def index = Action {Ok("Hello World Scala")}}
  • 101. GET /scala controllers.HelloWorldScala.index()Add it to the routes file as usualconf/routes
  • 102. http://localhost:9000/scala
  • 103. Play/Scala: the good parts
  • 104. Sequential async calls// 2 sequential async callsfor {foo <- WS.url(url1).get()bar <- WS.url(buildUrl(foo)).get()} yield {Ok(...)}API is more concise, expressive, &composable, especially for async code.Sequential async calls// 2 parallel async callsval fooFuture = WS.url(url1).get()val barFuture = WS.url(url2).get()for {foo <- fooFuturebar <- barFuture} yield {Ok(...)}
  • 105. Interactive console with full classpath of your app
  • 106. Play is Scala at its core: main libraries,routes, templates, SBT, etc [7]
  • 107. Play/Scala: the less good parts
  • 108. Simple Build Tool: Plays build system. Verypowerful, but very steep learning curve.
  • 109. project/Build.scalaval settings = Seq(bootPath <<= target( _ / "boot" ),buildDir <<= baseDirectory(_ / ".." / "build"),srcDir <<= baseDirectory(_/ ".." / "src"),enableConfigCompile := true,fabrics <++= Seq("EI", "EI2"))Abandon all hope, ye who enter herewithout knowing Scala.
  • 110. project/Build.scalafabrics <++= Seq("EI", "EI2")WTF is <++=? Some kind of fish bones?How do I google that?
  • 111. Outline1. Getting started with Play2. Make a change and reload3. Error handling4. Threaded vs. evented5. Non-blocking I/O6. Scala7. Performance8. Community
  • 112. Is Play web scale? [8]
  • 113. Scalability can be measured alongmany dimensions
  • 114. RawhorsepowerConcurrenthorsepowerSingledeveloperMultipledevelopers
  • 115. Raw horsepower: theoretical maximumperformance for the server in idealconditions. A measure of language andframework overhead.
  • 116. Concurrent horsepower: performance withmany users, I/O, and more real-worldscenarios. A measure of the frameworksapproach to concurrency.
  • 117. Single developer: how easy it is to getstarted, how quickly a single developer canbuild things. A measure of the frameworksraw productivity and tooling.
  • 118. Multiple developers: how the frameworktolerates many developers workingconcurrently over many years. A measure ofcode rot and maintainability.
  • 119. Here is how Id rate some of theframeworks Ive used. YMMV.
  • 120. ● Pros○ Good raw throughput (qps, latency)○ Type safety reduces code rot● Cons○ Not developer friendly. Getting things done takesforever.○ Threaded, synchronous approach difficult to scalefor lots of I/O in a SOA environment.MVC
  • 121. RawhorsepowerConcurrenthorsepowerSingledeveloperMultipledevelopersSpringMVC
  • 122. ● Pros○ Set the bar for developer productivity; all otherframeworks are still trying to catch up.● Cons○ Ruby is slow○ Ruby doesnt have real multithreading nor a greatevented framework○ Dynamic language makes it tougher to maintain alarge codebase
  • 123. RawhorsepowerConcurrenthorsepowerSingledeveloperMultipledevelopersRails
  • 124. ● Pros○ v8 engine is pretty fast for raw throughput○ Non-blocking I/O at the core makes concurrencyeasy○ Strong open source community and lightning faststartup time makes it easy to get things done quickly● Cons○ Dynamic language makes it tougher to maintain alarge codebase○ Lots of immature libraries that constantly makebackwards incompatible changes
  • 125. RawhorsepowerConcurrenthorsepowerSingledeveloperMultipledevelopersNode.js
  • 126. ● Pros○ Fast for raw throughput○ Non-blocking I/O at the core makes concurrencyeasy○ Hot reload makes it possible to get things donequickly○ Strong type safety throughout reduces code rot● Cons○ Even with hot reload, a compiled statically typedlanguage isnt quite as fast as an interpreteddynamically typed language
  • 127. RawhorsepowerConcurrenthorsepowerSingledeveloperMultipledevelopersPlay
  • 128. Outline1. Getting started with Play2. Make a change and reload3. Error handling4. Threaded vs. evented5. Non-blocking I/O6. Scala7. Performance8. Community
  • 129. Play is open source [7]
  • 130. LinkedIn is contributing
  • 131. Very active google group (mailing list) [9]
  • 132. StackOverflow tag [10]
  • 133. Open source modules and plugins [11]
  • 134. Weve open sourced a few of our plugins,with many more on the way [12]
  • 135. Commercial support from Typesafe [13]
  • 136. Recap1. Getting started with Play2. Make a change and reload3. Error handling4. Threaded vs. evented5. Non-blocking I/O6. Scala7. Performance8. Community
  • 137. Thats the story of Play at LinkedIn so far...
  • 138. But were just getting started.
  • 139. LinkedIn Engineering Blog [14]
  • 140. @LinkedInEng on twitter [15]
  • 141. Thank you!
  • 142. References1. http://zeroturnaround.com/java-ee-productivity-report-2011/2. http://static.springsource.org/spring/docs/2.5.x/api/org/springframework/...3. http://engineering.linkedin.com/play/play-framework-linkedin4. http://www.eecs.berkeley.edu/~rcs/research/interactive_latency.html5. http://engineering.linkedin.com/play/play-framework-async-io-without...6. http://netty.io/7. https://github.com/playframework/Play208. http://mongodb-is-web-scale.com/9. https://groups.google.com/forum/?fromgroups#!forum/play-framework10. http://stackoverflow.com/tags/playframework11. http://www.playframework.com/documentation/2.1.1/Modules12. https://github.com/linkedin13. http://typesafe.com/platform/runtime/playframework14. http://engineering.linkedin.com/15. https://twitter.com/LinkedInEng