Node.js vs Play Framework (with Japanese subtitles)

Post on 15-Jan-2015

24.161 views 3 download

description

Video: http://www.nicovideo.jp/watch/1410857293 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 version of the presentation has Japanese subtitles. For the English only version, see http://www.slideshare.net/brikis98/nodejs-vs-play-framework

Transcript of Node.js vs Play Framework (with Japanese subtitles)

Node.js vs Play Framework

VS

Node.js: server-side JavaScript runtime environment; open source; single threaded; non-blocking I/O.

Node.js: サーバサイドJSランタイム、OSS、シングルスレッド、非同期 I/O

express.js: the most popular web framework for Node.js.

express.js: Node.js で一番人気のWebフレームワーク

Play Framework: Java/Scala web framework; open source; multithreaded; non-blocking I/O.

Play: Java/Scala Webフレームワーク、OSS、マルチスレッド、非同期 I/O

Former Play Tech Lead at LinkedIn. Long time Node.js user.

Yevgeniy Brikman

元LinkedIn社Play Tech Lead。ベテランNode.jsユーザ

The framework scorecard

Learn

Develop

Test

Secure

Build

Deploy

Debug

Scale

Maintain

Share

1

For each feature we discuss...

Much worse than most frameworks

About the same as most frameworks

Much better than most frameworks

510

1 = 酷い、5 = 平均的、10 = 優秀

The framework scorecard

Learn

Develop

Test

Secure

Build

Deploy

Debug

Scale

Maintain

Share

Node.js: 1-click installers for every OSOSにかかわらずインストーラは万全

The “Hello World” Node app: 1 file, 6 lines of code.

var http = require('http');

http.createServer(function (req, res) {

res.writeHead(200, {'Content-Type': 'text/plain'});

res.end('Hello World\n');

}).listen(1337, '127.0.0.1');

console.log('Server running at http://127.0.0.1:1337/');

server.js

The “Hello World” Express app: 1 file, 8 lines of code.

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.js

Run using node <filename>. Starts instantly!

Hit http://localhost:1337 to test

And much, much more Tons of resources; very gradual learning curve.

リソースが豊富、緩やかな学習曲線

Learn

Develop

Test

Secure

Build

The framework scorecard

Deploy

Debug

Scale

Maintain

Share

10

Learn

Develop

Test

Secure

Build

The framework scorecard

Deploy

Debug

Scale

Maintain

Share

10

Play: download from playframework.com, extract, add activator to your PATH

Play:ダウンロードし、展開し、 activator をPATH に追加する

Generate a new app using activator new

The “Hello World” Play app: ~35 files and folders

Run the app using activator run

(Downloading all dependencies can take a while the first time around)

初回はjarのダウンロードに時間がかかる

Hit http://localhost:9000 to test

Ultimate Guide to Getting Started with Play. Not as many resources; steep learning curve.

リソースが少なめ、急勾配の学習曲線

Learn

Develop

Test

Secure

Build

The framework scorecard

Deploy

Debug

Scale

Maintain

Share

10 7

Learn

Develop

Test

Secure

Build

The framework scorecard

Deploy

Debug

Scale

Maintain

Share

10 7

Routing

GET clients/:id Clients.show(id: Long)

def show(id: Long) = Action { request =>

getClient(id).map { client =>

Ok(views.html.clients.show(client))

}

}

app.get('clients/:id', function(req, res) {

getClient(req.params.id, function(client) {

res.render('show', client);

});

});

RESTful routing. Extracts query & path params.

RESTful routing. Extracts query & path params. Type safe. Actions are composable. Reverse routing.

Templates

@(name: String, headline: String)

<div class="client">

<h1>@name</h1>

<div class="headline">

Headline: @headline

</div>

</div>

<div class="client">

<h1>{{name}}</h1>

<div class="headline">

Headline: {{headline}}

</div>

</div>

Many template options: handlebars, mustache, dust, jade, etc. Most support client-side rendering!

Twirl templates are compiled into Scala functions: type safe and composable! Other template types via plugins.

i18n

Translations: 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>

Form binding and validation

Forms, node-formidable, validator.js. Play form binding and validation API.

var regForm = forms.create({

name: fields.string({required: true}),

age: fields.number({min: 18})

});

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!")

)

JSON, XML, File Upload

bodyParser, xml2js, node-formidable. Play JSON, XML, File Upload APIs.

// Automatically parse application/json body

app.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.");

});

case 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")

}

POST /clients Clients.create

Data

Slick, Anorm, Ebean, JPAMySQL, MariaDB, PostgreSLQ, SQLite, Oracle, SQL Server,

DB2, Derby, H2SQLSequelize, Bookshelf.js, node-orm2

MySQL, MariaDB, PostgreSQL, SQLite

NoSQLmongojs/mongoose, cassandra-client, cradle/nano, node_redis, node-neo4j

MongoDB, Cassandra, CouchDB, Redis, Neo4j

ReactiveMongo, DataStax, sprouch, play-plugins-redis, Neo4j-play, JPA

MongoDB, Cassandra, CouchDB, Redis, Neo4j

Cachingnode-memcached, connect-cachememcached, in-memory (not recommended)

play2-memcached, memcontinuationed, Play Cache, ehcache, Guava

memcached, in-memory

Schemasnode-db-migrate, node-migrate Play database evolutions

Real-time web

socket.io: server & client APIs; WebSockets, Flash Sockets, polling, etc.

Play WebSockets, Comet, and EventSource APIs. Server-side only.

// server code

io.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 code

socket.emit('msg', 'Client says hi!');

socket.on('msg', function (msg) { … });

● Play is a full stack framework● Express.js is a minimal 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

Express.js:ミニマル、プラグインを多く使う。 Play:フルスタック

Learn

Develop

Test

Secure

Build

The framework scorecard

Deploy

Debug

Scale

Maintain

Share

10 7

8 10

Learn

Develop

Test

Secure

Build

The framework scorecard

Deploy

Debug

Scale

Maintain

Share

10 7

8 10

Functional testing: use supertest or call server.listen directly.

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);

Code coverage: Istanbul or Blanket.js

Learn

Develop

Test

Secure

Build

The framework scorecard

Deploy

Debug

Scale

Maintain

Share

10 7

8 10

10

Learn

Develop

Test

Secure

Build

The framework scorecard

Deploy

Debug

Scale

Maintain

Share

10 7

8 10

10

Functional testing: use Play’s built-in functional test helpers.

"respond to the index Action" in new App(FakeApplication()) {

val Some(result) = route(FakeRequest(GET, "/Bob"))

status(result) mustEqual OK

contentType(result) mustEqual Some("text/html")

charset(result) mustEqual Some("utf-8")

contentAsString(result) must include ("Hello Bob")

}

UI testing: use Play’s built-in integration test helpers and built-in Selenium support.

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")).value

eventually { pageTitle mustBe "scalatest" }

}

}

}

Code coverage: jacoco4sbt or scct

Learn

Develop

Test

Secure

Build

The framework scorecard

Deploy

Debug

Scale

Maintain

Share

10 7

8 10

10 10

Learn

Develop

Test

Secure

Build

The framework scorecard

Deploy

Debug

Scale

Maintain

Share

10 7

8 10

10 10

Learn

Develop

Test

Secure

Build

The framework scorecard

Deploy

Debug

Scale

Maintain

Share

10 7

8 10

10 10

86

Learn

Develop

Test

Secure

Build

The framework scorecard

Deploy

Debug

Scale

Maintain

Share

10 7

8 10

10 10

86

Node.js uses NPM to manage dependencies and basic build commands

{

"name": "Hello World App",

"version": "0.0.3",

"dependencies": {

"express": "4.8.0",

"underscore": "1.6.0"

},

"scripts": {

"test": "node tests/run.js"

}

}

Node.jsは依存性管理や簡単なコマンド定義にNPMを使う

Many options available for more complicated builds: grunt.js, gulp.js, or broccoli

より複雑なビルドの場合、 grunt.js, gulp.js, broccoliのようなツールを使う

Thousands of plugins for all common build tasksプラグインが何千もある

Learn

Develop

Test

Secure

Build

The framework scorecard

Deploy

Debug

Scale

Maintain

Share

10 7

8 10

10 10

86

10

Learn

Develop

Test

Secure

Build

The framework scorecard

Deploy

Debug

Scale

Maintain

Share

10 7

8 10

10 10

86

10

Play uses SBT as the build system. SBT is an interactive build system.

Play は SBT という対話的ビルドツールを利用

In SBT, build definitions are written in Scala! … But the learning curve is very steep.

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")

}

ビルドファイルをScalaで書く。でもビギナーには厳しい

Dependencies are managed using Ivy: familiar, but slow.

Ivyで依存性を管理する。使い慣れてるけど、実行性能は遅め

Play uses sbt-web to build static content: few plugins; depends on Rhino (slow) or node.js (!).

Playは静的コンテンツのビルドに sbt-webを利用する。プラグインが少ない

Learn

Develop

Test

Secure

Build

The framework scorecard

Deploy

Debug

Scale

Maintain

Share

10 7

8 10

10 10

86

10 7

Learn

Develop

Test

Secure

Build

The framework scorecard

Deploy

Debug

Scale

Maintain

Share

10 7

8 10

10 10

86

10 7

Use cluster to run one node instance per CPU

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);

}

cluster を使ってコア毎にインスタンスを稼働

Use forever, monit, or Domain to handle crashes.クラッシュ対処

Configuration: node-config or nconf

var config = require('config');

var host = config.get('dbConfig.host');

{

"dbConfig": {

"host": "localhost",

"port": 5984,

"dbName": "customers"

}

}

server.js

config/default.json

Use nginx, apache, or ATS to load balance, serve static content, terminate SSL

Client

Data Center

Reverse proxy(e.g. nginx) DB

Static server(e.g. nginx)

Node instanceNode

instanceNode instanceNode

instanceNode instance

Node instanceNode

instanceNode instanceNode

instance

Node instanceNode

instanceNode instanceNode

instance

ロードバランサー、静的コンテンツ、 SSL Termination

Learn

Develop

Test

Secure

Build

The framework scorecard

Deploy

Debug

Scale

Maintain

Share

10 7

8 10

10 10

86

10 7

8

Learn

Develop

Test

Secure

Build

The framework scorecard

Deploy

Debug

Scale

Maintain

Share

10 7

8 10

10 10

86

10 7

8

A few Play-friendly hosting options: Heroku, playframework-cloud, CloudBees

Playをサポートするホスティング

Use the SBT Native Packager to package the app as tgz, deb, RPM, etc.

Appをネイティブパッケージとして包装できる

Configuration: Play comes with Typesafe Config

val host = Play.current.configuration.getString("dbConfig.host")

dbConfig = {

host: "localhost",

port: 5984,

dbName: "customers"

}

app/controllers/Application.scala

conf/application.conf

Use nginx, apache, or ATS to load balance, serve static content, terminate SSL

Client

Data Center

Reverse proxy(e.g. nginx)

Play app

DB

Static server(e.g. nginx)

Play app

Play app

Learn

Develop

Test

Secure

Build

The framework scorecard

Deploy

Debug

Scale

Maintain

Share

10 7

8 10

10 10

86

10 7

8 7

Learn

Develop

Test

Secure

Build

The framework scorecard

Deploy

Debug

Scale

Maintain

Share

10 7

8 10

10 10

86

10 7

8 7

Node.js: use IntelliJ or node-inspector to debugNode.js:IntelliJでデバッグできる

Use winston, log4js, or bunyan for logging

var winston = require("winston");

winston.info("CHILL WINSTON! ... I put it in the logs.");

Learn

Develop

Test

Secure

Build

The framework scorecard

Deploy

Debug

Scale

Maintain

Share

10 7

8 10

10 10

86

10 7

8 7

10

Learn

Develop

Test

Secure

Build

The framework scorecard

Deploy

Debug

Scale

Maintain

Share

10 7

8 10

10 10

86

10 7

8 7

10

Play runs on the JVM, so you can use your favorite IDE to debug: IntelliJ, Eclipse, NetBeans

Play:好きなIDEを使える

In dev, Play shows errors right in the browser開発モードでエラーがブラウザで表示される

Learn

Develop

Test

Secure

Build

The framework scorecard

Deploy

Debug

Scale

Maintain

Share

10 7

8 10

10 10

86

10 7

8 7

10 10

Learn

Develop

Test

Secure

Build

The framework scorecard

Deploy

Debug

Scale

Maintain

Share

10 7

8 10

10 10

86

10 7

8 7

10 10

Part 1: scaling for lots of traffic

大量のトラフィックのためのスケーリング

TechEmpower benchmarks. Warning: microbenchmarks are not a substitute for real world perf testing!

LinkedIn experience #1: Play and Node.js are very fast in a service oriented architecture with NIO.

Internet Load Balancer

Frontend Server

Frontend Server

Frontend Server

Backend Server

Backend Server

Backend Server

Backend Server

Backend Server

Data Store

Data Store

Data Store

Data Store

LinkedInの感想:Node.jsもPlayもとても速い

LinkedIn experience #2: Play is ok with blocking I/O & CPU/memory bound use cases. Node.js is not.

// BAD: write files synchronously

fs.writeFileSync('message.txt', 'Hello Node');

console.log("It's saved, but you just blocked ALL requests!");

// Good: write files asynchronously

fs.writeFile('message.txt', 'Hello Node', function (err) {

console.log("It's saved and the server remains responsive!");

});

同期I/O、高メモリ使用率と高CPU使用率の場合、Node.jsの性能が落ちる

Part 2: scaling for large teams and projects

大きいチーム・プロジェクトのためのスケーリング

Node.js: best for small projects, short projects, small teams.

Node.js:小さいプロジェクト、短いプロジェクト、小さいチームにいい

Play: best for longer projects. Slower ramp up, but scales well with team & project size.

Play:より大きいプロジェクトにスケールできる

For comparison: Spring MVC

Learn

Develop

Test

Secure

Build

The framework scorecard

Deploy

Debug

Scale

Maintain

Share

10 7

8 10

10 10

86

10 7

8 7

10

10

10

10

Learn

Develop

Test

Secure

Build

The framework scorecard

Deploy

Debug

Scale

Maintain

Share

10 7

8 10

10 10

86

10 7

8 7

10

10

10

10

Maintenance: the good partsメンテナンス:「良いパーツ」

Functional programming: first class functions, closures, underscore.js

関数型プログラミング

JavaScript is ubiquitous...JSは至る所にある

...Which means you can share developers, practices, and even code: rendr, derby, meteor

そのため、デベロッパー、情報、コードは入手しやすい

Node core is stable and mature. Bugs, regressions, and backwards incompatibility are rare.

安定したコア。バグや後方互換性の問題は比較的少ない

Maintenance: the bad parts

メンテナンス:「悪いパーツ」

'' == '0' // false

0 == '' // true

0 == '0' // true

false == 'false' // false

false == '0' // true

false == undefined // false

false == null // false

null == undefined // true

' \t\r\n ' == 0 // true

Bad Parts

// Default scope is global

var foo = "I'm a global variable!"

// Setting undeclared variables puts them in global scope too

bar = "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

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

コールバック地獄

Many NPM packages are NOT stable or mature.Incompatibility + bugs + dynamic typing = pain.

NPMに不安定なパッケージが多い。非互換性 + バグ + 動的型付け = 苦痛

Learn

Develop

Test

Secure

Build

The framework scorecard

Deploy

Debug

Scale

Maintain

Share

10 7

8 10

10 10

86

10 7

8 7

10

10

10

10

3

Learn

Develop

Test

Secure

Build

The framework scorecard

Deploy

Debug

Scale

Maintain

Share

10 7

8 10

10 10

86

10 7

8 7

10

10

10

10

3

Maintenance: the good parts

Functional programming

def sort(a: List[Int]): List[Int] = {

if (a.length < 2) a

else {

val pivot = a(a.length / 2)

sort(a.filter(_ < pivot)) :::

a.filter(_ == pivot) :::

sort(a.filter(_ > pivot))

}

}

Powerful type system

Very expressive: case classes, pattern matching, lazy, option, implicits

val NameTagPattern = "Hello, my name is (.+) (.+)".r

val ListPattern = "Last: (.+). First: (.+).".r

// Case classes automatically generate immutable fields, equals, hashCode, constructor

case class Name(first: String, last: String)

// Use Option to avoid returning null if there is no name found

def extractName(str: String): Option[Name] = {

Option(str).collectFirst {

// Pattern matching on regular expressions

case NameTagPattern(fname, lname) => Name(fname, lname)

case ListPattern(lname, fname) => Name(fname, lname)

}

}

表現力が高い

Runs on the JVM; interop with Java.

No callback hell!

def index = Action {

// Make 3 sequential, async calls

for {

foo <- WS.url(url1).get()

bar <- WS.url(url2).get()

baz <- WS.url(url3).get()

} yield {

// Build a result using foo, bar, and baz

}

}

Good IDE support

Maintenance: the bad parts

Slow compilerコンパイラ遅い

Fortunately, Play/SBT support incremental compilation and hot reload!

Play/sbt はインクリメンタルコンパイラと hot reloading があるから大丈夫!

Complexity

More complexity

Play is stable, but not mature: backwards incompatible API changes every release.

Play は安定だがまだ成長期。APIはよく変わる

Even worse: Scala is not binary compatible between releases!

Scala はリリース間でバイナリ互換性を持たない!

Backwards incompatibility = pain.… Static typing makes it a little more manageable.

バイナリ互換性が無いのは辛いけど、静的な型によって少しは緩和される

Learn

Develop

Test

Secure

Build

The framework scorecard

Deploy

Debug

Scale

Maintain

Share

10 7

8 10

10 10

86

10 7

8 7

10

10

10

10

3 8

Learn

Develop

Test

Secure

Build

The framework scorecard

Deploy

Debug

Scale

Maintain

Share

10 7

8 10

10 10

86

10 7

8 7

10

10

10

10

3 8

351Contributors544

Watchers2,376 576

Stars31,332 5,077

Github activity as of 08/10/14

Forks6,970 1,872

PR’s3,066 2,201

10,698StackOverflow Questions53,555

Google Group Members14,199 11,577

Google Group Posts/Month ~400 ~1,100

StackOverflow, mailing list activity as of 08/12/14

88,000 packages in NPM ~80 Play Modules

88,000 packages in NPM 83,000 artifacts in Maven

Joyent offers commercial support for Node.js

Typesafe offers commercial support for Play

有償サポート

49LinkedIn1,172

Indeed3,605 179

CareerBuilder214 16

Open jobs as of 08/13/14採用募集数

9,037LinkedIn82,698

Indeed2,267 206

CareerBuilder447 30

Candidates as of 08/13/14採用候補者数

Learn

Develop

Test

Secure

Build

The framework scorecard

Deploy

Debug

Scale

Maintain

Share

10 7

8 10

10 10

86

10 7

8 7

10

10

10

10

3 8

10 7

Learn

Develop

Test

Secure

Build

The framework scorecard

Deploy

Debug

Scale

Maintain

Share

10 7

8 10

10 10

86

10 7

8 7

10

10

10

10

3 8

10 7

Final score

85

84

Final score

85

84

山田くん、これ全員の座布団全部持って行きなさい!

Both frameworks are great. Decide based on strengths/weakness, not my arbitrary score!

結論:どちらもすごい!

1. You’re building small apps with small teams

2. You already have a bunch of JavaScript ninjas

3. Your app is mostly client-side JavaScript

4. Your app is mostly real-time

5. Your app is purely I/O bound

Use node.js if:

向き: 小チーム小アプリ、JavaScript ニンジャ

1. You don’t write lots of automated tests

2. Your code base or team is going to get huge

3. You do lots of CPU or memory intensive tasks

Don’t use node.js if:

不向き: 自動テスト書かない人、大チーム、高 CPU か 高RAM

1. You’re already using the JVM

2. You like type safety and functional programming

3. Your code base or team is going to get big

4. You want a full stack framework

5. You need flexibility: non-blocking I/O, blocking I/O, CPU intensive tasks, memory intensive tasks

Use Play if:

向き: JVM ユーザ、型安全性、関数型が好き、大チーム

1. You don’t have time to master Play, Scala, and SBT

2. You hate functional programming or static typing

Don’t use Play if:

不向き: Play/Scala/sbt を勉強してる時間が無い、型安全性、関数型が嫌い

Questions?