Wednesday, November 9, 2011

An Erlang Application in 5 minutes

Introduction
The purpose of this tutorial is to get an erlang application up and running with as little work as possible.  The application will consist of 1 supervisor which monitors a simple server (the appliation will be called s_server).

There will be 1 worker process with a gen_server behaviour.  When this process receives a ping, it will respond with a pong.  

Kind of like the "hello world" of Erlang applications !

Vim Setup
Yes, I'm going to use vim to speed things up, why break a 20 year old habbit ?

I installed  vim-erlang-skeletons from https://github.com/aerosol/vim-erlang-skeletons.git.  It gives you well documented complete skeletons of erlang behaviours.  If you don't want to use vim, then you can just google for a copy of the relevant skeleton.




Installing rebar
I'm still new to rebar, but it's definately a great tool to have to speed up writing erlang applications.  It's going to save us quite a bit of manual work here.

$ mkdir -p ~/Programming/Erlang
$ cd ~/Programming/Erlang
$ git clone https://github.com/basho/rebar.git
$ cd rebar && make


Create a new directory for the application
The application will be called s_server:
$ mkdir ~/Programming/Erlang/s_server
$ cd ~/Programming/Erlang/s_server

Then copy the rebar executable into the myapp project dir
$ cp ../rebar/rebar .


Creating an OTP App
$ ./rebar create-app appid=s_server

The app directory then looks like this:
s_server
|-- rebar
`-- src
    |-- s_server_app.erl
    |-- s_server.app.src
    `-- s_server_sup.erl


$ ./rebar compile


This creates the ebin directory with the compiled code as well as the application specification.

s_server
|-- ebin
|   |-- s_server.app
|   |-- s_server_app.beam
|   `-- s_server_sup.beam
|-- rebar
`-- src
    |-- s_server_app.erl
    |-- s_server.app.src
    `-- s_server_sup.erl


The app spec in ebin/myapp.app is created from the template in
s_server/src/s_server.app.src

You just finished making an OPT application !

Starting the Application
Start an erlang shell and add the ebin to it's path:
$ erl -pa ebin

From the erlang shell
1> application:start(s_server).

Now the application is running.

Of course you didn't write any worker process, so the application does nothing, but you can use appman to check that the application is running:
2> appman:start()

When you are done, leave the erlang shell
3> q().

Writing a worker process for the s_server application
Start up vim and in vim type :ErlServer to get the skeleton of a gen_server behaviour.

Enter :w src/s_server.erl to save it (and you can compile here also if you wish just to check that everything is still fine).

In the first line of code, set the correct module name
-module(s_server).

Also useful is to add under the module definition:
-compile([export_all, debug_info]).


In the API section, add the following code:

ping() ->
        gen_server:call(ping).


In the callbacks section, delete the existing handle_call skeleton and replace it with:
handle_call(ping, _From, State) ->
        Reply = pong,
        {reply, Reply, State}.

Getting the supervisor module ready
From within vim open up src/s_server_sup.erl.  Delete all existing code and type :ErlSupervisor to replace the current code with a better template.

In the init function, you need to replace AModule with the name of the module which contains our code for the gen_server i.e. s_server:
%s/AModule/s_server/g


Start the Application

Save the file and compile with:
$ ./rebar compile

Startup the erlang shell
$ erl -pa ebin

Start the s_server application
1> application:start(s_server).
ok

Now test it out:
2> s_server:ping().
pong

And to prove that the supervisor is restarting the server when it crashes, try to kill it:

First get the process number of the s_server from the output of regs().

3> regs().
** Registered procs on node nonode@nohost **
Name                  Pid          Initial Call                      Reds Msgs
:
s_server              <0.39.0>     s_server:init/1                     32    0

Then send the exit signal to it;
4> exit(pid(0, 39, 0), kill).

You can then check again from the regs(). command the process id of s_server.  This should now be different as the exit signal restarted it.


1 comment:

justin said...

nice - didn't know about regs() and creating a pid like that. thx