luvit: node's ziggy stardust
I recently gave a talk at Open Source Bridge about
luvit, this great platform we are building that is like node.js
only using lua as the implementation language.
Anyways, my slides are available but I wanted to create a
slide by slide blog post with my notes too. So here goes!
Also, if you are in Portland for OSCON I will be giving a talk
about how we are building an on server monitoring agent for Rackspace
Cloud Monitoring on top of luvit. I will cover why we were interested in
using luvit, how luvit works and how we are embedding it in our
monitoring agent.
Untechnical Overview
Luvit is a platform for building your app in an event driven manner.
- Scrawny
- Awkward
- Space Themed (lua)
- <3 community
- Familiar node APIs
Notes: luvit is scrawny like Mr. Stardust and uses very little memory.
luvit is a young project and still growing, expect awkwardness. lua is
Portuguese for moon so it is space themed just like Ziggy. There is a
great community with a good sense of humor (luv_handles are a great data
structure name)
Technical Overview
- lua using luajit
- low memory footprint
- I/O driven event loop
- Small simple C API
- crypto, ssl, zlib, json bindings
- tcp, http, dns protocol support
- Windows, Linux, FreeBSD and OSX
Notes: luajit is a really tiny jit vm for lua, super fast. The event
loop is I/O driven like nodejs. Unlike node.js lua has a really simple C
APIc; this makes native modules quite a bit nicer to build. luvit has
the protocols and crypto you would expect. Also, runs on all major
platforms. Essentially a cross platform platform to build your
application.
HTTP Server Example
local http = require("http")
http.createServer(function (req, res)
local body = "Hello world\n"
res:writeHead(200, {
["Content-Type"] = "text/plain",
["Content-Length"] = #body
})
res:finish(body)
end):listen(8080)
print("Server listening at http://localhost:8080/")
Notes: This code works today. It serves up an HTTP 1.1. server on 8080
that tells you Hello!
Community
- http://luvit.io
- Use github pull requests/issues
- IRC on freenode #luvit
- Google group mailing list for discussion
- Apache 2.0 Licensed codebase
- Fun group of contributors
Notes: The community has the usual trappings of an open source
community. There is IRC, mailing lists and most development is driven
via github pull requests and issues.
History of the project
- Started by Tim Caswell
- Strong community of contributors
- Vladimir Dronnikov
- Ryan Phillips
- Paul Querna
- Brandon Philips (me)
- People taking the project in a variety of directions
- HTTP Application Servers
- SDL demos on Linux
- iPhone app development
- Cloud monitoring agent
Notes: There is a great community of people working on this project.
The best part is how many people are interested in different uses- not
just web stuff. In particular Rackspace is interested in a really small
memory footprint monitoring agent.
Comparison to node
<ul>
<li>256MB cloud machine running Linux</li>
<li>HTTP Server Benchmark</li>
<li>20 simultaneous requests</li>
<li>20000 total requests</li>
<li>Doing simple GET</li>
</ul>
<ul class="build">
<li>luvit - 3.09 MB - ~4100 requests/second</li>
<li>node - <b>51.99 MB</b> - ~4900 requests/second</li>
</ul>
Notes: Using a micro http benchmark you can see that luvit uses much
much less memory than node. The speed has regressed but hopefully we can
catch back up on the speed too :)
Lua
History
- Developed in Brazil at TeCGraf
- Related to a language called sol (sun)
- New language called lua (moon)
- Meta-mechanisms instead of features
- Really interesting history write-up
Notes: Lua has a really simple feature set that was kept small by
focusing on meta features instead of language features. We see that in
how luvit implements its Object system. Check out the history of lua for
a great history lesson
Lua - Javascript's Long Lost Brazilian Cousin
- Dynamic language
- Floating point numbers only
- First class functions
- Lexical closures
- Metatables
- Embeddable
Notes: Lua shares a lot of features with javascript like using floating
point numbers only, being dynamic and having first class functions. It
is like node's long lost Brazilian cousin
Example code
GroundControl = {}
function GroundControl.new()
obj = {}
obj.heard_major_tom = false
setmetatable(obj, { __index = GroundControl })
return obj
end
function GroundControl:heard()
print(self.heard_major_tom)
end
a = GroundControl.new()
a:heard()
a.heard() -- this will error
Notes: Using meta tables in lua you can implement an object system.
Essentially the meta table __index field tells lua "if you can't find
the requested element in this table try this table". In that way you can
implement Objects. One thing to note about lua is that the a:heard()
syntax passes in a as "self" to the heard function. Calling a.heard()
will error as self will be nil.
luajit Features
- x86, ARM, PPC, MIPS
- API compatible with Lua 5.1
- 125K for VM, 85K for JIT compiler
- JIT inlines FFI
Notes: luajit is an alternative tracing VM for lua that has a great FFI
layer. It is small and fast.
libuv
Basic idea
- Two types of events in the loop:
- I/O on file descriptors
- Timers for future events
- Callbacks are attached to these events
- epoll()/completion ports/kqueue() wait
- callback is called on the correct event
Notes: Essentially libuv is just a big loop (see the next section) that
runs poll on a bunch of file descriptors with the timeout of the poll
set to the next timer that needs to run. When the poll complete a
callback is made so the user can handle the event. I have talk on
libuv that covers all the details too
Event loop pseudo code
for (;;) {
nfds = poll(fds, maxfd, next_timer());
if (nfds == 0)
timer_callback();
for(n = 0; n < nfds; ++n) {
callbacks[fd]();
}
}
Other platforms built on libuv
Notes: A number of new platforms are using libuv. Rust is a new
language from mozilla. candor is a limited subset of javascript.
luvmonkey is mozilla's spidermonkey with libuv. http://julialang.org/
is also using libuv.
building luvit
Follow along at home
git clone git://github.com/luvit/luvit.git
cd luvit
gyp (all platforms)
./configure
make -C out
tools/build.py test
./out/Debug/luvit
Welcome to the Luvit repl
>
make (linux, embedded)
make
make test
./build/luvit
Welcome to the Luvit repl
>
luvit code practices
http://ifup.org/slides/luvit-osb/examples
Notes: This section will cover some examples of good looking luvit
code
Object system
local Object = require('core').Object
local Rectangle = Object:extend()
function Rectangle:initialize(w, h)
self.w = w
self.h = h
end
function Rectangle:getArea()
return self.w * self.h
end
local rect = Rectangle:new(3, 4)
p(rect:getArea())
Notes: Lua doesn't have an object system so we impelemted our own in
order to do our inheritance of stream, event emitter, etc
Module System
-- hello.lua
local hello = {}
hello.world = function()
print("Hello World")
end
return hello
-- run.lua
local hello = require('hello')
hello.world()
Notes: The module system is very similar to nodes. Essentially you
create a table of exported symbols and return it. leaking to global
scope is a bad practice require() returns a table that the imported file
returns
JSON Example
local JSON = require('json')
local value = JSON.parse([[
{
"author": "Brandon Philips <brandon@ifup.org>",
"name": "luvit - node's Ziggy Stardust",
"description": "a talk about luvit"
}
]])
local json = JSON.stringify(value, {beautify=true,indent_string=" "});
print(json)
Notes: Luvit has a JSON parser using yajl. It has some nice features
like allowing comments
HTTP Client
local http = require('http')
local options = {
host = "luvit.io",
port = 80,
path = "/"
}
local req = http.request(options, function (res)
res:on('data', function (chunk)
p("ondata", {chunk=chunk})
end)
end)
req:done()
Notes: This is a basic http client example. Try it against the server
from earlier :)
Users
Notes: There are a number of users of luvit today. Lets see some of
them
Modules
- luvit-mysql - MySQL binding
- luvit-redis - redis bindings
- curl - Simple HTTP request helper
- luvit-irc - IRC Client Library
- lua-msgpack-native - Fast MessagePack
- rounded - Connect middleware
- bourbon - async test runner
- checkit - simple test runner
Notes: there are lots of modules being built for luvit. And you know a
platform is getting popular when two test frameworks emerge!
Real world applications
- luvit.io is hosted using luvit
- Rackspace agent (see me at OSCON)
- Demos of using SDL/GL and Joystick interaction
Future work
- Documentation!
- Package management lum
- luvit as a library
- ports to OSX/iOS and Android event loops
Notes: Documentation is critical, we often rely on nodejs.org, eek
Thanks to the luvit community for all of their hard work. Also thanks to
Rackspace for letting me do work in the open.
Written by Brandon Philips
Related protips
1 Response
Hi Brandon,
First, thank you very much for this nice summary on luvit.
I'm starting to use luvit after I spent a few months using Lua + Xavante as a server-side web development platform. However, one thing I miss is being able to use a debugger. With Xavante, I use mobdebug to be able to set breakpoints in coroutines, and ZeroBrane studio as my debugger's UI.
I'm trying to do the same with luvit, but it seems I can't make mobdebug work with it. What debugger do you use when working with luvit?
Thank you!