xitrum web framework live coding demos / xitrum web framework ライブコーディング

42
Sept 06 2014

Upload: scalaconfjp

Post on 14-Jun-2015

466 views

Category:

Software


5 download

DESCRIPTION

Presentation material with Japanese subs, by at ScalaMatsuri 2014 http://scalamatsuri.org/en/

TRANSCRIPT

Page 1: Xitrum Web Framework Live Coding Demos / Xitrum Web Framework ライブコーディング

Sept 06 2014

Page 2: Xitrum Web Framework Live Coding Demos / Xitrum Web Framework ライブコーディング

Ngoc Dao

Takeharu Oshida

https://github.com/georgeOsdDev

https://github.com/ngocdaothanh

http://mobilus.co.jp/

Page 3: Xitrum Web Framework Live Coding Demos / Xitrum Web Framework ライブコーディング

What is

Xitrum?Xitrum is an async and clustered !Scala web framework and HTTP(S) server !on top of Netty, Akka

Page 4: Xitrum Web Framework Live Coding Demos / Xitrum Web Framework ライブコーディング

Why you should use

Xitrum?• Featureful!• Easy to use!• High performance

Scala, Netty, and Akka are fast!• Scalable

Can scale to a cluster of servers using Akka cluster and/or Hazelcast

Page 5: Xitrum Web Framework Live Coding Demos / Xitrum Web Framework ライブコーディング

Homepage:http://xitrum-framework.github.io/ (there are various demos) Guides (English, Japanese, Russian): http://xitrum-framework.github.io/guide.html (Korean version is in progress) !

Community (Google Group):https://groups.google.com/forum/#!forum/xitrum-framework

Page 6: Xitrum Web Framework Live Coding Demos / Xitrum Web Framework ライブコーディング

Where

Xitrum is used?KONNECT (Messaging Service)!http://mobilus.co.jp/konnect/!!

KONNECT can be used in mobile games, mobiles apps, SNS websites etc.Xitrum is also being used in France, Korea, Russia, Singapore etc.

Page 7: Xitrum Web Framework Live Coding Demos / Xitrum Web Framework ライブコーディング

Xitrum:!WebSocket (SockJS)!

CORS support

Page 8: Xitrum Web Framework Live Coding Demos / Xitrum Web Framework ライブコーディング

2010-2013 Xitrum 1.x-2.x

2014 Xitrum 3.x• Netty 4.x • Swagger • Component • FileMonitor, i18n • CORS • WebJARs • Glokka • Agent7 (autoreload classes on change)

!!

!

http://bit.ly/xitrum13

http://bit.ly/xitrum-changelog

Page 9: Xitrum Web Framework Live Coding Demos / Xitrum Web Framework ライブコーディング

How Xitrum works?

Page 10: Xitrum Web Framework Live Coding Demos / Xitrum Web Framework ライブコーディング

Client

Netty

Action FutureAction ActorAction

Akka

Xitrum

Thread pool to run FutureAction and

ActorAction

I/O thread pool to accept requests and reply

responses

Client

Run directly on Netty I/O thread

Dispatch request

Async

Netty handler

Netty handler

Netty handler

Netty handler

Xitrum

Your program

Page 11: Xitrum Web Framework Live Coding Demos / Xitrum Web Framework ライブコーディング

http://bit.ly/xitrum-handlers

Page 12: Xitrum Web Framework Live Coding Demos / Xitrum Web Framework ライブコーディング

Akka cluster (code)!Hazelcast (data)

Client

Netty

A FA AA

Akka

Xitrum

ClientClient

Netty

A FA AA

Akka

Xitrum

Client

Server N Server N+1

Page 13: Xitrum Web Framework Live Coding Demos / Xitrum Web Framework ライブコーディング

Embed Xitrumobject MyApp { def main(args: Array[String]) { ... // Somewhere in your app xitrum.Server.start() ... } }

1. Collect routes!!

2. Start HTTP/HTTPS servers

Page 14: Xitrum Web Framework Live Coding Demos / Xitrum Web Framework ライブコーディング

import xitrum.Action import xitrum.annotation.GET !

@GET("hello") class MyAction extends Action { def execute() { respondText("Hello") } }

Action example

FutureAction!ActorAction

Page 15: Xitrum Web Framework Live Coding Demos / Xitrum Web Framework ライブコーディング

Annotations: Scala vs JavaScala: @GET("matsuri", "festival") Java: @GET(Array("matsuri", "festival")) !

Scala: case class GET(paths: String*) extends

scala.annotation.StaticAnnotation !

Java:public @interface GET { String[] value(); }

Page 16: Xitrum Web Framework Live Coding Demos / Xitrum Web Framework ライブコーディング

Benefits of using annotations

Routes in .class and .jar in classpath are automatically collected and merged.

A.class

B.class

lib1.jar

lib2.jar

Routes

Page 17: Xitrum Web Framework Live Coding Demos / Xitrum Web Framework ライブコーディング

Problem with annotationsCollecting routes from .class and .jar files is slow.!!

Solutions:!• In development mode, routes in .class and .jar

files that are not in the current working directory are cached to file routes.cache.

• To avoid loading lots of classes, don't collect routes from: java.xxx, javax.xxx, scala.xxx, sun.xxx, com.sun.xxx

Page 18: Xitrum Web Framework Live Coding Demos / Xitrum Web Framework ライブコーディング

Annotations defined by Xitrum:!http://bit.ly/xitrum-annotations!!

Lib to scan classes in classpath to collect routes:!https://github.com/xitrum-framework/sclasner

Page 19: Xitrum Web Framework Live Coding Demos / Xitrum Web Framework ライブコーディング

username

password

login

Hello ! Hello! How are you? Fine

message send

GET /chatGET /

POST /login SockJS /connect

Demo overview

https://github.com/xitrum-framework/matsuri14

Page 20: Xitrum Web Framework Live Coding Demos / Xitrum Web Framework ライブコーディング

• Simple HTTP CRUD with MongoDB POST /admin/user GET /admin/user GET /admin/user/:userId PUT /admin/user/:userId DELETE /admin/user/:userIdPUT/PATCH/DELETE can be emulated viaPOST with _method=PUT/PATCH/DELETE

!• API documentation with Swagger

Demo overview

Page 21: Xitrum Web Framework Live Coding Demos / Xitrum Web Framework ライブコーディング

DB Cluseter

Xitrum3

Cluster

LB (HAProxy, Nginx,

Route53 etc.)

NoSQL

RDB

Other services

Akka Hazelcast

Xitrum2 Akka Hazelcast

Xitrum1 Akka Hazelcast

https://github.com/xitrum-framework/glokka https://github.com/xitrum-framework/xitrum-hazelcast

Page 22: Xitrum Web Framework Live Coding Demos / Xitrum Web Framework ライブコーディング
Page 23: Xitrum Web Framework Live Coding Demos / Xitrum Web Framework ライブコーディング

Getting started with xitrum-new skeletonhttps://github.com/xitrum-framework/xitrum-new

https://github.com/xitrum-framework/xitrum-scalate

Page 24: Xitrum Web Framework Live Coding Demos / Xitrum Web Framework ライブコーディング

@GET("admin") class AdminIndex extends Action { def execute() { // Get all users val users = User.listAll() // Pass users to view template at("users") = users !

// Response respons view with template respondView() } }

ActorActionFutureAction

Action

Page 25: Xitrum Web Framework Live Coding Demos / Xitrum Web Framework ライブコーディング

- import matsuri.demo.action._!- import matsuri.demo.model.User!!div.row#usersTable! table.table.table-striped#messageTable! thead! tr.bg-primary! th.col-xs-2 =t("Name")! th.col-xs-2 =t("Age")! th.col-xs-2 =t("Desc")! th.col-xs-2 =t("Created time")! th.col-xs-2 =t("Updated time")! th.col-xs-2 =t("Last login time")! tbody! - for (user <- at("users").asInstanceOf[List[User]])! tr! th! a(href={url[AdminUserShow](("name", user.name))}) = user.name! th = user.age! th = user.desc! th = user.createdAtAsStr! th = user.updatedAtAsStr! th = user.lastLoginAsStr

• Scalate template with jade (mustache, scaml, or ssp)

• "at" function • i18n with GNU gettext

View

Page 26: Xitrum Web Framework Live Coding Demos / Xitrum Web Framework ライブコーディング

└── src └── scalate └── matsuri └── demo └── action ├── AdminIndex.jade └── DefaultLayout.jade

package matsuri.demo.action !

import xitrum.Action !

trait DefaultLayout extends Action { override def layout = renderViewNoLayout[DefaultLayout]() }

Layout

Page 27: Xitrum Web Framework Live Coding Demos / Xitrum Web Framework ライブコーディング

!!! 5 html head != antiCsrfMeta != xitrumCss ! meta(content="text/html; charset=utf-8" http-equiv="content-type") title ScalaMatsuri2014 Xitrum Demo ! link(rel="shortcut icon" href={publicUrl("favicon.ico")}) link(type="text/css" rel="stylesheet" media="all" href={webJarsUrl("bootstrap/3.2.0/css", "bootstrap.css", "bootstrap.min.css")}) link(type="text/css" rel="stylesheet" media="all" href={publicUrl("app.css")}) ! body .container h1 ! #flash !~ jsRenderFlash() != renderedView ! != jsDefaults script(src={webJarsUrl("bootstrap/3.2.0/js", "bootstrap.js", "bootstrap.min.js")}) script(src={webJarsUrl("underscorejs/1.6.0", "underscore.js", "underscore-min.js")}) != jsForView

Layout

Page 28: Xitrum Web Framework Live Coding Demos / Xitrum Web Framework ライブコーディング

form(role="form" method="post" action={url[AdminUserCreate]}) != antiCsrfInput div.modal-header button.close(type="button" data-dismiss="modal") span(aria-hidden="true") &times; span.sr-only =t("Close") h4.modal-title#myModalLabel =t("Create New User") div.modal-body div.form-group label(for="newUserName") =t("Name") input.form-control#newUserName(name="name" type="text" placeholder={t("Enter Name")} minlength=5 maxlenght=10 required=true) div.form-group label(for="newUserPass") =t("Password") input.form-control#newUserPass(name="password" type="password" placeholder={t("Enter Password")} minlength=8 required=true) ! div.modal-footer button.btn.btn-default(type="button" data-dismiss="modal") = t("Cancel") button.btn.btn-primary(type="submit") = t("Save")

Form• "url" function • jquery-validation • Anti csrf token

Page 29: Xitrum Web Framework Live Coding Demos / Xitrum Web Framework ライブコーディング

@POST("admin/user") class AdminUserCreate extends AdminAction { def execute() { // Get request paramaters val name = param("name") val password = param("password") // Optional parameters val age = paramo[Int]("age") val desc = paramo("desc") ! Required.exception("name", name) Required.exception("password", password) ! User.create(name, password, age, desc) flash(t("Success")) redirectTo[AdminIndex]() }

Get request params

with param(s)

and param(o)

Page 30: Xitrum Web Framework Live Coding Demos / Xitrum Web Framework ライブコーディング

trait AdminFilter { this: Action => ! beforeFilter { if (SVar.isAdmin.isDefined) true else authBasic() } ! private def authBasic(): Boolean = { basicAuth(Config.basicAuth.realm) { (username, password) => if (username == Config.basicAuth.name && password == Config.basicAuth.pass) { SVar.isAdmin.set(true) true } else { false } } }

object SVar { object isAdmin extends SessionVar[Boolean] }

Use before filter to check authentication info in session

Page 31: Xitrum Web Framework Live Coding Demos / Xitrum Web Framework ライブコーディング

@Swagger( Swagger.Summary("Create User"), Swagger.Response(200, "status = 0: success, 1: failed to create user"), Swagger.Response(400, "Invalid request parameter"), Swagger.StringForm("name"), Swagger.StringForm("password"), Swagger.OptIntForm("age"), Swagger.OptStringForm("desc") )

• /xitrum/swagger • /xitrum/swagger-ui • Create test client with Swagger-codegen

https://github.com/wordnik/swagger-codegen

API doc

https://github.com/wordnik/swagger-ui

https://github.com/wordnik/swagger-spec

Page 32: Xitrum Web Framework Live Coding Demos / Xitrum Web Framework ライブコーディング

@GET("login", "") class LoginIndex extends DefaultLayout { def execute() { respondView() } } !@POST("login") class Login extends Action { def execute() { session.clear() val name = param("name") val password = param("password") ! User.authLogin(name, password) match { case Some(user) => SVar.userName.set(user.name) redirectTo[ChatIndex]() ! case None => flash(t(s"Invalid username or password")) redirectTo[LoginIndex]() } } }

Login

Page 33: Xitrum Web Framework Live Coding Demos / Xitrum Web Framework ライブコーディング

jsAddToView( "var url = '" + sockJsUrl[ChatAction] + "';" + """ var socket; var initSocket = function() { socket = new SockJS(url); socket.onopen = function(event) { console.log("socket onopen", event.data); socket.send(JSON.parse({"msg":"Hello Xitrum"})); }; socket.onclose = function(event) {console.log("socket onclose", event.data);}; socket.onmessage = function(event) {console.log("socket onmessage", event.data);}; }; initSocket(); """ )

• jsAddToView/jsForView • sockJsUrl • webJarsUrl

!= jsDefaults script(src={webJarsUrl("bootstrap/3.2.0/js", "bootstrap.js", "bootstrap.min.js")}) script(src={webJarsUrl("underscorejs/1.6.0", "underscore.js", "underscore-min.js")}) != jsForView

Create chat

client with

SockJS

Page 34: Xitrum Web Framework Live Coding Demos / Xitrum Web Framework ライブコーディング

import xitrum.{SockJsAction, SockJsText} import xitrum.annotation.SOCKJS !@SOCKJS("connect") class ChatAction extends SockJsAction with LoginFilter { def execute() { context.become { case SockJsText(text) => SeriDeseri.fromJson[Map[String, String]](text) match { case Some(jsonMap) => // echo respondSockJsText(SeriDeseri.toJson(jsonMap)) case None => log.warn(s"Failed to parse request: $text") respondSockJsText("invalid request") } }

• SockJsAction • SockJsText/respondSockJsText • SeriDeseri.fromJson[T] / SeriDeseri.toJson(ref:AnyRef)

Create SockJsAction

(an Actor)

Page 35: Xitrum Web Framework Live Coding Demos / Xitrum Web Framework ライブコーディング

Lookup singleton Actor with Glokka

Xitrum

Client

ChatAction ChatAction ChatAction ChatAction

HubActor

Client Client Client

trait Hub extends Actor { protected var clients = Seq[ActorRef]() def receive = { case Subscribe(option) => clients = clients :+ sender case Unsubscribe(option) => clients = clients.filterNot(_ == sender) case Terminated(client) => clients = clients.filterNot(_ == client) case ignore => }

import glokka.Registry object Hub { val KEY_PROXY = "HUB_PROXY" val actorRegistry = Registry.start(Config.actorSystem, KEY_PROXY) } !def lookUpHub(key: String, hubProps: Props, option: Any = None) { Hub.actorRegistry ! Registry.Register(key, hubProps) context.become { case result: Registry.FoundOrCreated => result.ref ! Subscribe } }

https://github.com/xitrum-framework/glokka

hub ! Subscribe

socket open

Page 36: Xitrum Web Framework Live Coding Demos / Xitrum Web Framework ライブコーディング

Messaging overviewClient ChatAction HubActor

https://github.com/georgeOsdDev/glokka-demo

case class Done (option: Map[String, Any] = Map.empty) // Hub -> Action case class Publish(option: Map[String, Any] = Map.empty) // Hub -> Action case class Pull (option: Map[String, Any] = Map.empty) // Action -> Hub case class Push (option: Map[String, Any] = Map.empty) // Action -> Hub

SockJsText!(socket.send) hub ! Push(msg)

ClientChatAction

ChatAction

ChatAction

clients.foreach { _ ! Publish(msg)} respondSockJSText(msg:String)

Client

Client

sender ! Done(msg)respondSockJSText(msg:String)

Client ChatAction HubActor

SockJsText hub ! Pull(msg)

sender ! Done(msg)respondSockJSText(msg:String)

socket.onmessagesocket.onmessage

Page 37: Xitrum Web Framework Live Coding Demos / Xitrum Web Framework ライブコーディング

Cluster config for Hazelcast hazelcastMode = clusterMember ! cache = xitrum.hazelcast.Cache #cache { # # Simple in-memory cache # "xitrum.local.LruCache" { # maxElems = 10000 # } #} ! session { store = xitrum.hazelcast.Session # Store sessions on client side #store = xitrum.scope.session.CookieSessionStore

xitrum.conf• Xitrum-hazelcast • Shared Session • Shared Cache

Page 38: Xitrum Web Framework Live Coding Demos / Xitrum Web Framework ライブコーディング

akka { loggers = ["akka.event.slf4j.Slf4jLogger"] logger-startup-timeout = 30s ! actor { provider = "akka.cluster.ClusterActorRefProvider" } ! # This node remote { log-remote-lifecycle-events = off netty.tcp { hostname = "127.0.0.1" port = 2551 # 0 means random port } } ! cluster { seed-nodes = [ "akka.tcp://[email protected]:2551", "akka.tcp://[email protected]:2552"] ! auto-down-unreachable-after = 10s } }

Cluster config

for Akka

Page 39: Xitrum Web Framework Live Coding Demos / Xitrum Web Framework ライブコーディング

Live class reload during developmenthttps://github.com/xitrum-framework/agent7

https://github.com/dcevm/dcevm

java -javaagent:`dirname $0`/agent7-1.0.jar-XXaltjvm=dcevm -Xms256M -Xmx512M -Xss1M -XX:+CMSClassUnloadingEnabled -XX:MaxPermSize=384M-jar `dirname $0`/sbt-launch-0.13.5.jar "$@"

https://github.com/xitrum-framework/xitrum-package

Page 40: Xitrum Web Framework Live Coding Demos / Xitrum Web Framework ライブコーディング

Package project for deploying to production server

sbt/sbt xitrum-package

https://github.com/xitrum-framework/xitrum-package

Page 41: Xitrum Web Framework Live Coding Demos / Xitrum Web Framework ライブコーディング

Monitor Xitrum in production mode

• Scalive • Metrics • Log to fluentd

https://github.com/xitrum-framework/scalive http://www.slideshare.net/georgeosd/scalive http://xitrum-framework.github.io/guide/3.18/en/log.html#log-to-fluentd

Page 42: Xitrum Web Framework Live Coding Demos / Xitrum Web Framework ライブコーディング

Thank you!