...

Node.js vs Play Framework

by yevgeniy-brikman

on

Report

Category:

Software

Download: 0

Comment: 0

54,659

views

Comments

Description

Video: https://www.youtube.com/watch?v=b6yLwvNSDck

Here's the showdown you've been waiting for: Node.js vs Play Framework. Both are popular open source web frameworks that are built for developer productivity, asynchronous I/O, and the real time web. But which one is easier to learn, test, deploy, debug, and scale? Should you pick Javascript or Scala? The Google v8 engine or the JVM? NPM or Ivy? Grunt or SBT? Two frameworks enter, one framework leaves.

This is the English version of the presentation. For the version with Japanese subtitles, see http://www.slideshare.net/brikis98/nodejs-vs-play-framework-with-japanese-subtitles
Download Node.js vs Play Framework

Transcript

  • 1. VSNode.js vs Play Framework
  • 2. Node.js: server-side JavaScript runtime environment;open source; single threaded; non-blocking I/O.
  • 3. express.js: the most popular webframework for Node.js.
  • 4. Play Framework: Java/Scala web framework; opensource; multithreaded; non-blocking I/O.
  • 5. Yevgeniy BrikmanFormer Play Tech Lead at LinkedIn. Long time Node.js user.
  • 6. The framework scorecardLearnDevelopTestSecureBuildDeployDebugScaleMaintainShare
  • 7. For each feature we discuss...1Much worse than most frameworksAbout the same as most frameworksMuch better than most frameworks510
  • 8. The framework scorecardLearnDevelopTestSecureBuildDeployDebugScaleMaintainShare
  • 9. Node.js: 1-click installers for every OS
  • 10. var http = require('http');http.createServer(function (req, res) {res.writeHead(200, {'Content-Type': 'text/plain'});res.end('Hello Worldn');}).listen(1337, '127.0.0.1');console.log('Server running at http://127.0.0.1:1337/');server.jsThe “Hello World” Node app: 1 file, 6 lines of code.
  • 11. var express = require('express');var app = express();app.get('/', function(req, res){res.send('Hello World');});var server = app.listen(1337, function() {console.log('Listening on port %d', server.address().port);});server.jsThe “Hello World” Express app: 1 file, 8 lines of code.
  • 12. Run using node <filename>. Starts instantly!
  • 13. Hit http://localhost:1337 to test
  • 14. nodeschool.io
  • 15. Node Beginner Book, Mastering Node.js,Node: Up and Running, Node.js in Action
  • 16. Node API Docs
  • 17. Express.js guide
  • 18. Express Web Application Development Express.js Guide
  • 19. Express.js API docs
  • 20. And much, much moreTons of resources; very gradual learning curve.
  • 21. LearnDevelopTestSecureBuildThe framework scorecardDeployDebugScaleMaintainShare10
  • 22. LearnDevelopTestSecureBuildThe framework scorecardDeployDebugScaleMaintainShare10
  • 23. Play: download from playframework.com,extract, add activator to your PATH
  • 24. Generate a new app using activator new
  • 25. The “Hello World” Play app: ~35 files and folders
  • 26. Run the app using activator run
  • 27. (Downloading all dependencies can take awhile the first time around)
  • 28. Hit http://localhost:9000 to test
  • 29. Play Framework Documentation
  • 30. Activator Templates
  • 31. Play for Scala Learning Play Framework 2
  • 32. Ultimate Guide to Getting Started with Play.Not as many resources; steep learning curve.
  • 33. LearnDevelopTestSecureBuildThe framework scorecardDeployDebugScaleMaintainShare10 7
  • 34. LearnDevelopTestSecureBuildThe framework scorecardDeployDebugScaleMaintainShare10 7
  • 35. GET clients/:id Clients.show(id: Long)def show(id: Long) = Action { request =>getClient(id).map { client =>Ok(views.html.clients.show(client))}}Routingapp.get('clients/:id', function(req, res) {getClient(req.params.id, function(client) {res.render('show', client);});});RESTful routing. Extracts query & pathparams.RESTful routing. Extracts query & pathparams. Type safe. Actions arecomposable. Reverse routing.
  • 36. @(name: String, headline: String)<div class="client"><h1>@name</h1><div class="headline">Headline: @headline</div></div>Templates<div class="client"><h1>{{name}}</h1><div class="headline">Headline: {{headline}}</div></div>Many template options: handlebars,mustache, dust, jade, etc. Most supportclient-side rendering!Twirl templates are compiled into Scalafunctions: type safe and composable!Other template types via plugins.
  • 37. i18nTranslations: i18next-node, i18n-node.Formatting: moment.js, numeral.js.Translations: Play i18n API.Formatting: Java formatting libraries.<div class="client"><h1>{{name}}</h1><div class="headline">{{t "headline.label" headline=headline}}</div></div>@(name: String, headline: String)<div class="client"><h1>@name</h1><div class="headline">Messages("headline.label", headline)</div></div>
  • 38. var regForm = forms.create({name: fields.string({required: true}),age: fields.number({min: 18})Forms, node-formidable, validator.js. Play form binding and validation API.Form binding and validation});regForm.handle(req, {success: function(form) { ... },error: function(form) { ... }});val regForm = Form(mapping("name" -> nonEmptyText,"age" -> number(min = 18))(UserData.apply)(UserData.unapply))regForm.bindFromRequest.fold(err => BadRequest("Validation error"),data => Ok(s"Hi $data.name!"))
  • 39. // Automatically parse application/json bodyapp.use(bodyParser.json());app.post('/clients', function (req, res, next) {var name = req.body.name;var age = req.body.age;res.send(name + " is " + age + " years old.");});POST /clients Clients.createcase class Person(name: String, age: Int)implicit val prsnFmt = Json.format[Person]def create = Action(parse.json) { request =>val person = request.body.as[Person]Ok(s"$person.name is $person.age years old")}bodyParser, xml2js, node-formidable. Play JSON, XML, File Upload APIs.JSON, XML, File Upload
  • 40. DataSlick, Anorm, Ebean, JPAMySQL, MariaDB, PostgreSLQ, SQLite, Oracle, SQL Server,DB2, Derby, H2Sequelize, Bookshelf.js, node-orm2 SQLMySQL, MariaDB, PostgreSQL, SQLiteNoSQLmongojs/mongoose, cassandra-client,cradle/nano, node_redis, node-neo4jMongoDB, Cassandra, CouchDB, Redis, Neo4jReactiveMongo, DataStax, sprouch, play-plugins-redis, Neo4j-play, JPAMongoDB, Cassandra, CouchDB, Redis, Neo4jnode-memcached, connect-cache Cachingmemcached, in-memory (not recommended)play2-memcached, memcontinuationed,Play Cache, ehcache, Guavamemcached, in-memorynode-db-migrate, node-migrate Schemas Play database evolutions
  • 41. socket.io: server & client APIs;WebSockets, Flash Sockets, polling, etc.Play WebSockets, Comet, andEventSource APIs. Server-side only.Real-time web// server codeio.on('connection', function (socket) {socket.emit('msg', 'Server says hi!');socket.on('msg', function (msg) { … });});def chat = WebSocket.acceptWithActor {request => out => Props(new Chat(out))}class Chat(out: ActorRef) extends Actor {def receive = {case m: String => out ! s"Got msg: $m"}}// client codesocket.emit('msg', 'Client says hi!');socket.on('msg', function (msg) { … });
  • 42. ● Express.js is a minimal framework ● Play is a full stack framework● You need plugins for most tasks● Finding good plugins takes time● Gluing plugins together takes time● There are defaults for most tasks● Defaults are mostly high quality● All defaults can be replaced
  • 43. LearnDevelopTestSecureBuildThe framework scorecardDeployDebugScaleMaintainShare10 78 10
  • 44. LearnDevelopTestSecureBuildThe framework scorecardDeployDebugScaleMaintainShare10 78 10
  • 45. Unit testing: Jasmine, Mocha, QUnit,nodeunit, Expresso or Vows
  • 46. var request = require('supertest'), app = require('express')();app.get('/user', function(req, res){res.send(200, { name: 'tobi' });});request(app).get('/user').expect('Content-Type', /json/).expect(200);Functional testing: use supertest or callserver.listen directly.
  • 47. UI testing: phantom.js or zombie.js
  • 48. Code coverage: Istanbul or Blanket.js
  • 49. LearnDevelopTestSecureBuildThe framework scorecardDeployDebugScaleMaintainShare10 78 1010
  • 50. LearnDevelopTestSecureBuildThe framework scorecardDeployDebugScaleMaintainShare10 78 1010
  • 51. Unit testing: junit, ScalaTest, specs2, or testng
  • 52. "respond to the index Action" in new App(FakeApplication()) {val Some(result) = route(FakeRequest(GET, "/Bob"))status(result) mustEqual OKcontentType(result) mustEqual Some("text/html")charset(result) mustEqual Some("utf-8")contentAsString(result) must include ("Hello Bob")}Functional testing: use Play’s built-infunctional test helpers.
  • 53. class ExampleSpec extends PlaySpec with OneServerPerSuite with OneBrowserPerSuite {"The OneBrowserPerTest trait" must {"provide a web driver" in {go to (s"http://localhost:$port/testing")pageTitle mustBe "Test Page"click on find(name("b")).valueeventually { pageTitle mustBe "scalatest" }}}}UI testing: use Play’s built-in integration testhelpers and built-in Selenium support.
  • 54. Code coverage: jacoco4sbt or scct
  • 55. LearnDevelopTestSecureBuildThe framework scorecardDeployDebugScaleMaintainShare10 78 1010 10
  • 56. LearnDevelopTestSecureBuildThe framework scorecardDeployDebugScaleMaintainShare10 78 1010 10
  • 57. (not enabled by default!) Connect CSRF Middleware CSRF(not enabled by default!)Depends on template engine XSS Twirl escapes correctlyVulnerabilities: eval, setTimeout, InjectionSecurityCSRFFiltersetInterval, new Function Few vulnerabilities of this sortHelmet middleware Headers SecurityHeadersFilterpassport.js, everyauth Auth SecureSocial, deadbolt, play-authenticateNode Security Project Advisories Play Security Vulnerabilities
  • 58. LearnDevelopTestSecureBuildThe framework scorecardDeployDebugScaleMaintainShare10 78 1010 106 8
  • 59. LearnDevelopTestSecureBuildThe framework scorecardDeployDebugScaleMaintainShare10 78 1010 106 8
  • 60. {"name": "Hello World App","version": "0.0.3","dependencies": {"express": "4.8.0","underscore": "1.6.0"},"scripts": {"test": "node tests/run.js","lint": "jshint src"}}Node.js uses NPM to manage dependencies andbasic build commands
  • 61. Many options available for more complicatedbuilds: grunt.js, gulp.js, or broccoli
  • 62. Thousands of plugins for all common build tasks
  • 63. LearnDevelopTestSecureBuildThe framework scorecardDeployDebugScaleMaintainShare10 78 1010 106 810
  • 64. LearnDevelopTestSecureBuildThe framework scorecardDeployDebugScaleMaintainShare10 78 1010 106 810
  • 65. Play uses SBT as the build system. SBT is aninteractive build system.
  • 66. object AppBuild extends Build {lazy val root = Project(id = "root", base = file(".")).settings(name := "test-play-app",version := version,libraryDependencies += Seq("org.scala-tools" % "scala-stm_2.11.1" % "0.3","org.apache.derby" % "derby" % "10.4.1.3"))def version = Option(System.getProperty("version")).getOrElse("0.0.3")}In SBT, build definitions are written in Scala!… But the learning curve is very steep.
  • 67. Dependencies are managed using Ivy:familiar, but slow.
  • 68. Play uses sbt-web to build static content: fewplugins; depends on Rhino (slow) or node.js (!).
  • 69. LearnDevelopTestSecureBuildThe framework scorecardDeployDebugScaleMaintainShare10 78 1010 106 810 7
  • 70. LearnDevelopTestSecureBuildThe framework scorecardDeployDebugScaleMaintainShare10 78 1010 106 810 7
  • 71. Many node-friendly hosting options: Heroku,Joyent, Azure, OpenShift, Nodejitsu
  • 72. Monitoring: New Relic, Nodetime,AppDynamics, node-monitor
  • 73. if (cluster.isMaster) {for (var i = 0; i < numCPUs; i++) {cluster.fork(); // Fork workers}cluster.on('exit', function(worker, code, signal) {console.log('worker ' + worker.process.pid + ' died');});} else {http.createServer(function(req, res) {// ...}).listen(8000);}Use cluster to run one node instance per CPU
  • 74. Use forever, monit, or Domain to handle crashes.
  • 75. {"dbConfig": {"host": "localhost","port": 5984,"dbName": "customers"}}config/default.jsonvar config = require('config');var host = config.get('dbConfig.host');server.jsConfiguration: node-config or nconf
  • 76. Use nginx, apache, or ATS to load balance,serve static content, terminate SSLClientData CenterReverse proxy(e.g. nginx) DBStatic server(e.g. nginx)NodeinstNaondceeinstNaondceeinstNaondceeinstNaondceeinstanceNodeinstNaondceeinstNaondceeinstNaondceeinstanceNodeinstNaondceeinstNaondceeinstNaondceeinstance
  • 77. LearnDevelopTestSecureBuildThe framework scorecardDeployDebugScaleMaintainShare10 78 1010 106 810 78
  • 78. LearnDevelopTestSecureBuildThe framework scorecardDeployDebugScaleMaintainShare10 78 1010 106 810 78
  • 79. A few Play-friendly hosting options: Heroku,playframework-cloud, CloudBees
  • 80. Monitoring: New Relic, metrics-play
  • 81. Use the SBT Native Packager to package theapp as tgz, deb, RPM, etc.
  • 82. dbConfig = {host: "localhost",port: 5984,dbName: "customers"}conf/application.confval host = Play.current.configuration.getString("dbConfig.host")app/controllers/Application.scalaConfiguration: Play comes with Typesafe Config
  • 83. Use nginx, apache, or ATS to load balance,serve static content, terminate SSLClientData CenterReverse proxy(e.g. nginx)Play appDBStatic server(e.g. nginx)Play appPlay app
  • 84. LearnDevelopTestSecureBuildThe framework scorecardDeployDebugScaleMaintainShare10 78 1010 106 810 78 7
  • 85. LearnDevelopTestSecureBuildThe framework scorecardDeployDebugScaleMaintainShare10 78 1010 106 810 78 7
  • 86. Node.js: use IntelliJ or node-inspector to debug
  • 87. Use DTrace, TraceGL, node-stackviz, and node-profilerto debug perf issues
  • 88. var winston = require("winston");winston.info("CHILL WINSTON! ... I put it in the logs.");Use winston, log4js, or bunyan for logging
  • 89. LearnDevelopTestSecureBuildThe framework scorecardDeployDebugScaleMaintainShare10 78 1010 106 810 78 710
  • 90. LearnDevelopTestSecureBuildThe framework scorecardDeployDebugScaleMaintainShare10 78 1010 106 810 78 710
  • 91. Play runs on the JVM, so you can use yourfavorite IDE to debug: IntelliJ, Eclipse, NetBeans
  • 92. In dev, Play shows errors right in the browser
  • 93. Use YourKit, VisualVM, BTrace, and jmap todebug perf issues
  • 94. Use logback, slf4j, or log4j for logging
  • 95. LearnDevelopTestSecureBuildThe framework scorecardDeployDebugScaleMaintainShare10 78 1010 106 810 78 710 10
  • 96. LearnDevelopTestSecureBuildThe framework scorecardDeployDebugScaleMaintainShare10 78 1010 106 810 78 710 10
  • 97. Part 1: scaling for lots of traffic
  • 98. TechEmpower benchmarks.Warning: microbenchmarks are not a substitute for real world perf testing!
  • 99. JSON serialization
  • 100. Single query(Note: JDBC is blocking!)
  • 101. Multiple queries(Note: JDBC is blocking!)
  • 102. Internet LoadBalancerFrontendServerFrontendServerFrontendServerBackendServerBackendServerBackendServerBackendServerBackendServerDataStoreDataStoreDataStoreDataStoreLinkedIn experience #1: Play and Node.js are veryfast in a service oriented architecture with NIO.
  • 103. // BAD: write files synchronouslyfs.writeFileSync('message.txt', 'Hello Node');console.log("It's saved, but you just blocked ALL requests!");// Good: write files asynchronouslyfs.writeFile('message.txt', 'Hello Node', function (err) {console.log("It's saved and the server remains responsive!");});LinkedIn experience #2: Play is ok with blocking I/O& CPU/memory bound use cases. Node.js is not.
  • 104. Part 2: scaling for large teams and projects
  • 105. Node.js: best for small projects, shortprojects, small teams.
  • 106. Play: best for longer projects. Slower ramp up, butscales well with team and project size.
  • 107. For comparison: Spring MVC
  • 108. LearnDevelopTestSecureBuildThe framework scorecardDeployDebugScaleMaintainShare10 78 1010 106 810 78 710101010
  • 109. LearnDevelopTestSecureBuildThe framework scorecardDeployDebugScaleMaintainShare10 78 1010 106 810 78 710101010
  • 110. Maintenance: the good parts
  • 111. Functional programming: first class functions,closures, underscore.js
  • 112. JavaScript is ubiquitous...
  • 113. ...Which means you can share developers,practices, and even code: rendr, derby, meteor
  • 114. Node core is (mostly) stable and mature. Bugs,regressions, and backwards incompatibility are rare.
  • 115. Maintenance: the bad parts
  • 116. '' == '0' // false0 == '' // true0 == '0' // truefalse == 'false' // falsefalse == '0' // truefalse == undefined // falsefalse == null // falsenull == undefined // true' trn ' == 0 // trueBad Parts
  • 117. // Default scope is globalvar foo = "I'm a global variable!"// Setting undeclared variables puts them in global scope toobar = "I'm also a global variable!";if (foo) {// Look ma, no block scope!var baz = "Believe it or not, I'll also be a global variable!"}Awful Parts
  • 118. Wat
  • 119. this keyword
  • 120. doSomethingAsync(req1, function(err1, res1) {doSomethingAsync(req2, function(err2, res2) {doSomethingAsync(req3, function(err3, res3) {doSomethingAsync(req4, function(err4, res4) {// ...});});});});Callback hell: control flow, error handling,and composition are all difficult
  • 121. Many NPM packages are NOT stable or mature.Incompatibility + bugs + dynamic typing = pain.
  • 122. LearnDevelopTestSecureBuildThe framework scorecardDeployDebugScaleMaintainShare10 78 1010 106 810 78 7101010103
  • 123. LearnDevelopTestSecureBuildThe framework scorecardDeployDebugScaleMaintainShare10 78 1010 106 810 78 7101010103
  • 124. Maintenance: the good parts
  • 125. def sort(a: List[Int]): List[Int] = {if (a.length < 2) aelse {val pivot = a(a.length / 2)sort(a.filter(_ < pivot)) :::a.filter(_ == pivot) :::sort(a.filter(_ > pivot))Functional programming}}
  • 126. Powerful type system
  • 127. val NameTagPattern = "Hello, my name is (.+) (.+)".rval ListPattern = "Last: (.+). First: (.+).".r// Case classes automatically generate immutable fields, equals, hashCode, constructorcase class Name(first: String, last: String)// Use Option to avoid returning null if there is no name founddef extractName(str: String): Option[Name] = {Option(str).collectFirst {// Pattern matching on regular expressionscase NameTagPattern(fname, lname) => Name(fname, lname)case ListPattern(lname, fname) => Name(fname, lname)}}Very expressive: case classes, patternmatching, lazy, option, implicits
  • 128. Runs on the JVM; interop with Java.
  • 129. Concurrency & streaming tools: Futures, Akka,STM, threads, Scala.rx, Async/Await, Iteratees
  • 130. def index = Action {// Make 3 sequential, async callsfor {foo <- WS.url(url1).get()bar <- WS.url(url2).get()baz <- WS.url(url3).get()} yield {// Build a result using foo, bar, and baz}}No callback hell!
  • 131. Good IDE support
  • 132. Maintenance: the bad parts
  • 133. Slow compiler
  • 134. Fortunately, Play/SBT support incrementalcompilation and hot reload!
  • 135. Complexity
  • 136. More complexity
  • 137. Play is stable, but not mature: backwardsincompatible API changes every release.
  • 138. Even worse: Scala is not binary compatiblebetween releases!
  • 139. Backwards incompatibility = pain.… Static typing makes it a little more manageable.
  • 140. LearnDevelopTestSecureBuildThe framework scorecardDeployDebugScaleMaintainShare10 78 1010 106 810 78 7101010103 8
  • 141. LearnDevelopTestSecureBuildThe framework scorecardDeployDebugScaleMaintainShare10 78 1010 106 810 78 7101010103 8
  • 142. 544 Contributors 3512,376 Watchers 57631,332 Stars 5,0776,970 Forks 1,8723,066 PR’s 2,201Github activity as of 08/10/14
  • 143. StackOverflow 10,69853,555 QuestionsGoogle Group14,199 Members 11,577Google Group~400 Posts/Month ~1,100StackOverflow, mailing list activity as of 08/12/14
  • 144. 4 langpop.com 1810 TIOBE 395 CodeEval 127 IEEE Spectrum 171 RedMonk 1312 Lang-Index 26Language Popularity as of 08/12/14
  • 145. 88,000 packages in NPM ~80 Play Modules
  • 146. 88,000 packages in NPM 83,000 artifacts in Maven
  • 147. Joyent offers commercialsupport for Node.jsTypesafe offers commercialsupport for Play
  • 148. Node.js in production
  • 149. Play in production
  • 150. 1,172 LinkedIn 493,605 Indeed 179214 CareerBuilder 16Open jobs as of 08/13/14
  • 151. 82,698 LinkedIn 9,0372,267 Indeed 206447 CareerBuilder 30Candidates as of 08/13/14
  • 152. LearnDevelopTestSecureBuildThe framework scorecardDeployDebugScaleMaintainShare10 78 1010 106 810 78 7101010103 810 7
  • 153. LearnDevelopTestSecureBuildThe framework scorecardDeployDebugScaleMaintainShare10 78 1010 106 810 78 7101010103 810 7
  • 154. Final score8584
  • 155. Final score8584
  • 156. Both frameworks are great. Decide based onstrengths/weakness, not my arbitrary score!
  • 157. Use node.js if:1. You’re building small apps with small teams2. You already have a bunch of JavaScript ninjas3. Your app is mostly client-side JavaScript4. Your app is mostly real-time5. Your app is purely I/O bound
  • 158. Don’t use node.js if:1. You don’t write lots of automated tests2. Your code base or team is going to get huge3. You do lots of CPU or memory intensive tasks
  • 159. Use Play if:1. You’re already using the JVM2. You like type safety and functional programming3. Your code base or team is going to get big4. You want a full stack framework5. You need flexibility: non-blocking I/O, blocking I/O,CPU intensive tasks, memory intensive tasks
  • 160. Don’t use Play if:1. You don’t have time to master Play, Scala, and SBT2. You hate functional programming or static typing
  • 161. Questions?
  • Fly UP