Running Julia on Azure Websites

Warning: This is a complete hack

screenshot

TL;DR

A node process hosts the julia server, and passes the name of the named pipe to it as an argument.

Some Background on IISNode

Azure websites hosts node.js apps with IISNode. This basically does two things, activate the node process, and forward HTTP requests to is using a named pipe.

Websites doesn’t restrict you to hosting JavaScript files, in fact you can start any old binary. I have had some success in hosting Go on Websites before (using node to forward requests using CGI).

Julia

Julia is a language that’s been around for a couple of years. It’s general purpose, with a strength in mathematical programming. I really like the syntax, and it seems to fit with the way I think about programming.

Julia has a web stack, with Morsel.jl as the ‘Sinatra like’ web framework. I’m playing around with some of my own ideas, built on the same stack. I’ve called it Jolt.jl.

Julia sits on the same networking library (libuv) as node, so I thought it would be simple to get it running on Azure. I was wrong.

Hosting on Azure Websites

Let’s go through the steps I took to get it working.

  • You need to bring the Julia runtime with you, so copy the bin and lib folder from %HOME_DIRECTORY%\AppData\Local\Julia-0.3.2 into your repository.
  • You need to bring the packages too, so let’s copy them in as well from your %HOME_DIRECTORY%\.julia directory.
  • Delete all the .git folders from the packages, and remove all the .gitignore files.
  • We need to tell Julia to look locally for the packages, so you’ll need to add this line to the start of your program push!(LOAD_PATH, ".") (we’ll do this in a bit).
  • You need to rewrite part of the HTTPServer module, so it uses PipeServer instead of TcpServer. This allows us to respond to requests on the named pipe. This will look something like this:
immutable HttpHandler
    handle::Function
    sock::Base.UVServer
    events::Dict

    HttpHandler(handle::Function) = new(handle, Base.PipeServer(), defaultevents)
end
  • You’ll need to write a new run method, to bind your named pipe to the PipeServer:
export run_pipe

function run_pipe(server::Server, pipe::ASCIIString)
    id_pool = 0 # Increments for each connection
    sock = server.http.sock
    websockets_enabled = server.websock != nothing
    Base.uv_error("listen", !Base.bind(sock::Base.PipeServer, pipe))
    listen(sock)
    event("listen", server, 0)

    while true # handle requests, Base.wait_accept blocks until a connection is made
        client = Client(id_pool += 1, accept(sock))
        client.parser = ClientParser(message_handler(server, client, websockets_enabled))
        @async process_client(server, client, websockets_enabled)
    end
end
  • Remove all traces of GnuTLS from HttpServer (remove the using statement and the run_https function). It’s more trouble than it’s worth!
  • Fix up paths in any deps.jl files which point to binaries on your local machine, and set to "D:\home\site\wwwroot\* instead.
  • Now you can write a program. I used my Jolt.jl framework, but you can use what you like…
push!(LOAD_PATH, ".")

using HttpServer
using Jolt
using JoltView
using JoltJson

app = jolt()

app.get("/") do req, res, ctx
    "hello world"
end

http = HttpHandler(app.dispatch) 
server = Server(http)
run_pipe(server, ASCIIString(ARGS[1].data))
  • My attempts to start the program directly from IISNode failed. Instead I wrote a node app (server.js) to start the Julia App.
var spawn = require('child_process').spawn;
console.log("starting julia");

var env = process.env;
env["HOMEDRIVE"] = "C:";
env["HOMEPATH"] = "\home\site\wwwroot";

var julia = spawn('bin\julia.exe', 
    ["server.jl", process.env.port], 
    { 
        env : env,
        cwd : process.cwd()
    },
    function callback(error, stdout, stderr){

});

julia.stdout.on('data', function (data) {
  console.log(data);
});

julia.stderr.on('data', function (data) {
  console.log(data);
});

julia.on('close', function (code) {
  console.log('Julia exited with code ' + code);
});
  • Note that it’s necessary to add a couple of missing environment variables when starting the process.
  • Push to Azure Websites and stand back!

Alternatively, you can just clone my repo which seems to work.

Advertisements