Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
Sept 06 2014
Ngoc Dao 
https://github.com/ngocdaothanh 
Takeharu Oshida 
https://github.com/georgeOsdDev 
http://mobilus.co.jp/
What is 
Xitrum? 
Xitrum is an async and clustered ! 
Scala web framework and HTTP(S) server ! 
on top of Netty, Akka
Why you should use 
Xitrum? 
• Featureful! 
• Easy to use! 
• High performance 
Scala, Netty, and Akka are fast! 
• Scalab...
Homepage: 
http://xitrum-framework.github.io/ 
(there are various demos) 
Guides (English, Japanese, Russian): 
http://xit...
Where 
Xitrum is used? 
KONNECT (Messaging Service)! 
http://mobilus.co.jp/konnect/! 
! 
KONNECT can be used in mobile gam...
Xitrum:! 
WebSocket (SockJS)! 
CORS support
2010-2013 Xitrum 1.x-2.x 
http://bit.ly/xitrum13 
2014 Xitrum 3.x 
• Netty 4.x 
• Swagger 
• Component 
• FileMonitor, i18...
How Xitrum 
works?
Client 
Netty 
Async 
Dispatch 
request 
Action FutureAction ActorAction 
Akka 
Xitrum 
I/O thread pool to 
accept request...
http://bit.ly/xitrum-handlers
Client 
Client Client 
Akka cluster (code)! 
Hazelcast (data) 
Client 
Netty 
Xitrum 
A FA AA 
Akka 
Netty 
Xitrum 
A FA A...
Embed Xitrum 
object MyApp { 
def main(args: Array[String]) { 
... 
// Somewhere in your app 
xitrum.Server.start() 
... 
...
Action example 
import xitrum.Action 
import xitrum.annotation.GET 
! 
@GET("hello") 
class MyAction extends Action { 
def...
Annotations: Scala vs Java 
Scala: @GET("matsuri", "festival") 
Java: @GET(Array("matsuri", "festival")) 
! 
Scala: 
case ...
Benefits of using annotations 
Routes in .class and .jar in classpath are 
automatically collected and merged. 
A.class 
B...
Problem with annotations 
Collecting routes from .class and .jar files is slow.! 
! 
Solutions:! 
• In development mode, r...
Annotations defined by Xitrum:! 
http://bit.ly/xitrum-annotations! 
! 
Lib to scan classes in classpath to collect routes:...
Demo overview 
GET / GET /chat 
username 
password 
login 
Hello 
! 
Hello! 
How are you? 
Fine 
message send 
POST /login...
Demo overview 
• Simple HTTP CRUD with MongoDB 
POST /admin/user 
GET /admin/user 
GET /admin/user/:userId 
PUT /admin/use...
DB Cluseter 
Xitrum1 Akka Hazelcast 
Xitrum2 Akka Hazelcast 
Xitrum3 
Cluster 
LB 
(HAProxy, Nginx, 
Route53 etc.) 
NoSQL ...
Getting started 
with xitrum-new 
skeleton 
https://github.com/xitrum-framework/xitrum-new 
https://github.com/xitrum-fram...
@GET("admin") 
class AdminIndex extends Action { 
def execute() { 
// Get all users 
val users = User.listAll() 
// Pass u...
- import matsuri.demo.action._! 
- import matsuri.demo.model.User! 
! 
div.row#usersTable! 
table.table.table-striped#mess...
└── src 
└── scalate 
└── matsuri 
└── demo 
└── action 
├── AdminIndex.jade 
└── DefaultLayout.jade 
package matsuri.demo...
!!! 5 
html 
head 
!= antiCsrfMeta 
!= xitrumCss 
! 
meta(content="text/html; charset=utf-8" http-equiv="content-type") 
t...
form(role="form" method="post" action={url[AdminUserCreate]}) 
!= antiCsrfInput 
div.modal-header 
button.close(type="butt...
@POST("admin/user") 
class AdminUserCreate extends AdminAction { 
def execute() { 
// Get request paramaters 
val name = p...
object SVar { 
object isAdmin extends SessionVar[Boolean] 
} 
Use before filter 
to check 
trait AdminFilter { 
this: Acti...
@Swagger( 
Swagger.Summary("Create User"), 
Swagger.Response(200, "status = 0: success, 1: failed to create user"), 
Swagg...
@GET("login", "") 
class LoginIndex extends DefaultLayout { 
def execute() { 
respondView() 
} 
} 
! 
@POST("login") 
clas...
jsAddToView( 
"var url = '" + sockJsUrl[ChatAction] + "';" + 
""" 
var socket; 
var initSocket = function() { 
socket = ne...
import xitrum.{SockJsAction, SockJsText} 
import xitrum.annotation.SOCKJS 
! 
@SOCKJS("connect") 
class ChatAction extends...
Lookup 
singleton 
Actor 
with 
Glokka 
Xitrum 
socket open 
Client 
HubActor 
trait Hub extends Actor { 
protected var cl...
Messaging overview 
SockJsText! 
(socket.send) hub ! Push(msg) 
clients.foreach { _ ! Publish(msg)} respondSockJSText(msg:...
Cluster config for Hazelcast 
hazelcastMode = clusterMember 
! 
cache = xitrum.hazelcast.Cache 
#cache { 
# # Simple in-me...
akka { 
loggers = ["akka.event.slf4j.Slf4jLogger"] 
logger-startup-timeout = 30s 
! 
actor { 
provider = "akka.cluster.Clu...
Live class reload 
during development 
https://github.com/xitrum-framework/agent7 
https://github.com/dcevm/dcevm 
java -j...
Package project for 
deploying to 
production server 
https://github.com/xitrum-framework/xitrum-package 
sbt/sbt xitrum-p...
Monitor Xitrum 
in production mode 
• Scalive 
• Metrics 
• Log to fluentd 
https://github.com/xitrum-framework/scalive 
h...
Thank 
you!
Xitrum Web Framework Live Coding Demos / Xitrum Web Framework ライブコーディング
Upcoming SlideShare
Loading in …5
×

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

2,080 views

Published on

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

Published in: Software
  • Be the first to comment

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

  1. 1. Sept 06 2014
  2. 2. Ngoc Dao https://github.com/ngocdaothanh Takeharu Oshida https://github.com/georgeOsdDev http://mobilus.co.jp/
  3. 3. What is Xitrum? Xitrum is an async and clustered ! Scala web framework and HTTP(S) server ! on top of Netty, Akka
  4. 4. 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
  5. 5. 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
  6. 6. 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.
  7. 7. Xitrum:! WebSocket (SockJS)! CORS support
  8. 8. 2010-2013 Xitrum 1.x-2.x http://bit.ly/xitrum13 2014 Xitrum 3.x • Netty 4.x • Swagger • Component • FileMonitor, i18n • CORS • WebJARs • Glokka • Agent7 (autoreload classes on change) ! ! http://bit.ly/xitrum-changelog !
  9. 9. How Xitrum works?
  10. 10. Client Netty Async Dispatch request Action FutureAction ActorAction Akka Xitrum I/O thread pool to accept requests and reply responses Thread pool to run FutureAction and ActorAction Client Run directly on Netty I/O thread Netty handler Netty handler Netty handler Netty handler Xitrum Your program
  11. 11. http://bit.ly/xitrum-handlers
  12. 12. Client Client Client Akka cluster (code)! Hazelcast (data) Client Netty Xitrum A FA AA Akka Netty Xitrum A FA AA Akka Server N Server N+1
  13. 13. Embed Xitrum object MyApp { def main(args: Array[String]) { ... // Somewhere in your app xitrum.Server.start() ... } 1. Collect routes! } ! 2. Start HTTP/HTTPS servers
  14. 14. Action example import xitrum.Action import xitrum.annotation.GET ! @GET("hello") class MyAction extends Action { def execute() { respondText("Hello") } } FutureAction! ActorAction
  15. 15. Annotations: Scala vs Java Scala: @GET("matsuri", "festival") Java: @GET(Array("matsuri", "festival")) ! Scala: case class GET(paths: String*) extends scala.annotation.StaticAnnotation ! Java: public @interface GET { String[] value(); }
  16. 16. 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
  17. 17. Problem with annotations Collecting 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
  18. 18. Annotations defined by Xitrum:! http://bit.ly/xitrum-annotations! ! Lib to scan classes in classpath to collect routes:! https://github.com/xitrum-framework/sclasner
  19. 19. Demo overview GET / GET /chat username password login Hello ! Hello! How are you? Fine message send POST /login SockJS /connect https://github.com/xitrum-framework/matsuri14
  20. 20. Demo overview • Simple HTTP CRUD with MongoDB POST /admin/user GET /admin/user GET /admin/user/:userId PUT /admin/user/:userId DELETE /admin/user/:userId PUT/PATCH/DELETE can be emulated via POST with _method=PUT/PATCH/DELETE ! • API documentation with Swagger
  21. 21. DB Cluseter Xitrum1 Akka Hazelcast Xitrum2 Akka Hazelcast Xitrum3 Cluster LB (HAProxy, Nginx, Route53 etc.) NoSQL RDB Other services Akka Hazelcast https://github.com/xitrum-framework/glokka https://github.com/xitrum-framework/xitrum-hazelcast
  22. 22. Getting started with xitrum-new skeleton https://github.com/xitrum-framework/xitrum-new https://github.com/xitrum-framework/xitrum-scalate
  23. 23. @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() } } ActorAction FutureAction Action
  24. 24. - import matsuri.demo.action._! - import matsuri.demo.model.User! ! div.row#usersTable! table.table.table-striped#messageTable! thead! tr.bg-primary! View • Scalate template with jade (mustache, scaml, or ssp) • "at" function • i18n with GNU gettext 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
  25. 25. └── 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
  26. 26. !!! 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 Layout ! != 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
  27. 27. form(role="form" method="post" action={url[AdminUserCreate]}) != antiCsrfInput div.modal-header button.close(type="button" data-dismiss="modal") Form 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" • "url" function • jquery-validation • Anti csrf token 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")
  28. 28. @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)
  29. 29. object SVar { object isAdmin extends SessionVar[Boolean] } Use before filter to check 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 } } } authentication info in session
  30. 30. @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") ) API doc • /xitrum/swagger • /xitrum/swagger-ui • Create test client with Swagger-codegen https://github.com/wordnik/swagger-ui https://github.com/wordnik/swagger-codegen https://github.com/wordnik/swagger-spec
  31. 31. @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
  32. 32. 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(); """ ) != 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 • jsAddToView/jsForView • sockJsUrl • webJarsUrl Create chat client with SockJS
  33. 33. import xitrum.{SockJsAction, SockJsText} import xitrum.annotation.SOCKJS ! @SOCKJS("connect") class ChatAction extends SockJsAction with LoginFilter { def execute() { context.become { SockJsAction (an Actor) 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") } Create } • SockJsAction • SockJsText/respondSockJsText • SeriDeseri.fromJson[T] / SeriDeseri.toJson(ref:AnyRef)
  34. 34. Lookup singleton Actor with Glokka Xitrum socket open Client HubActor 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 { hub ! Subscribe ChatAction ChatAction ChatAction ChatAction case result: Registry.FoundOrCreated => result.ref ! Subscribe } } Client Client Client https://github.com/xitrum-framework/glokka
  35. 35. Messaging overview SockJsText! (socket.send) hub ! Push(msg) clients.foreach { _ ! Publish(msg)} respondSockJSText(msg:String) Client ChatAction HubActor socket.onmessage SockJsText hub ! Pull(msg) 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 https://github.com/georgeOsdDev/glokka-demo ChatAction Client ChatAction ChatAction Client Client respondSockJSText(msg:String) sender ! Done(msg) Client ChatAction HubActor respondSockJSText(msg:String) sender ! Done(msg) socket.onmessage
  36. 36. Cluster config for Hazelcast hazelcastMode = clusterMember ! cache = xitrum.hazelcast.Cache #cache { # # Simple in-memory cache # "xitrum.local.LruCache" { xitrum.conf # maxElems = 10000 # } • Xitrum-hazelcast #} • Shared Session • Shared Cache ! session { store = xitrum.hazelcast.Session # Store sessions on client side #store = xitrum.scope.session.CookieSessionStore
  37. 37. 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://xitrum@127.0.0.1:2551", "akka.tcp://xitrum@127.0.0.1:2552"] ! auto-down-unreachable-after = 10s } } Cluster config for Akka
  38. 38. Live class reload during development https://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
  39. 39. Package project for deploying to production server https://github.com/xitrum-framework/xitrum-package sbt/sbt xitrum-package
  40. 40. 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
  41. 41. Thank you!

×