Erlang
初心者
入門
rebar3

今からでも間に合うErlang入門

More than 1 year has passed since last update.

Ubuntu 16.04でErlangのインストールから簡単なサンプルアプリケーションの作成まで行ってみます。


Erlang開発環境構築(初期設定)


依存ファイルをインストール

sudo apt-get install build-essential libncurses5-dev openssl libssl-dev

sudo apt-get install curl git-core


Erlangのバージョン管理、Kerlをインストール

mkdir -p ~/local/bin

cd ~/local/bin
curl -O https://raw.githubusercontent.com/kerl/kerl/master/kerl
chmod a+x kerl


kerlでErlang 19.1をインストール

./kerl list releases

./kerl install 19.1 19.1
./kerl build 19.1 19.1
./kerl install 19.1 ~/erlang/19.1

インストールした Erlang 19.1 を有効化します。

. ~/erlang/19.1/activate

whichでerlコマンドが使える事が分かり開発環境が整いました。

which erl

# /home/username/erlang/19.1/bin/erl


簡単なネットワークアプリケーションの開発

Erlangで簡単なhttpdサーバを作ってみます。


ビルドツールのrebarを使う

rebarを使うとコンパイル、テスト、配布用のtar.gzの作成などがコマンド一つでできるので便利です。

mkdir project

cd project
wget https://s3.amazonaws.com/rebar3/rebar3
chmod 0755 rebar3


rebarで定形ファイル作成

./rebar3 new app httpd

# ===> Writing httpd/src/httpd_app.erl
# ===> Writing httpd/src/httpd_sup.erl
# ===> Writing httpd/src/httpd.app.src
# ===> Writing httpd/rebar.config
# ===> Writing httpd/.gitignore
# ===> Writing httpd/LICENSE
# ===> Writing httpd/README.md


実装

今回は簡単なhttpサーバを実装してみます。

処理の流れは以下のイメージ。


  1. httpd_tcp_listener が新規接続してきたHTTPクライアントを受付

  2. httpd_tcp_worker_sup の start_child() で1つのTCPストリームを処理するプロセスを起動(Erlangのプロセスはマイクロスレッドを意味します)

  3. 起動したプロセスは httpd_tcp_worker の start_link() でTCPストリームからHTTPリクエストを読み込み、HTTPレスポンスを書き込む

以下の3ファイルを追加します。


src/httpd_tcp_listener.erl

-module(httpd_tcp_listener).

-export([start_link/0]).

start_link() ->
Pid = spawn_link(fun init/0),
{ok, Pid}.

init() ->
Port = 8888,
Backlog = 10244,
Options = [binary,
inet6, % support both ipv4 and ipv6
{active, false},
{reuseaddr, true},
{backlog, Backlog}
],
{ok, Listen} = gen_tcp:listen(Port, Options),
accept(Listen).

accept(Listen) ->
case gen_tcp:accept(Listen) of
{ok, Socket} ->
{ok, Pid} = httpd_tcp_worker_sup:start_child(Socket),
gen_tcp:controlling_process(Socket, Pid);
{error, Reason} ->
io:format("fail accept ~p~n", [Reason])
end,
accept(Listen).



src/httpd_tcp_worker.erl

-module(httpd_tcp_worker).

-export([start_link/1]).

start_link(Socket)->
Pid = spawn_link(fun() -> process(Socket, 0, 30 * 1000) end),
{ok, Pid}.

process(Socket, Size, Timeout) ->
case gen_tcp:recv(Socket, Size, Timeout) of
{ok, Packet} ->
Response = response(Packet),
gen_tcp:send(Socket, Response),
gen_tcp:close(Socket);
{error, Reason} ->
io:format("fail recv ~p~n", [Reason]),
gen_tcp:close(Socket)
end.

response(_Request) ->
<<"HTTP/1.0 200 OK\r\nDate: Tue, 25 Oct 2016 10:21:33 GMT\r\nConnection: close\r\nContent-Type: text/plain; charset=utf-8\r\nContent-Length: 11\r\n\r\nhello world">>.



src/httpd_tcp_worker_sup.erl

-module(httpd_tcp_worker_sup).

-export([start_link/0, start_child/1, init/1]).

start_link() ->
supervisor:start_link({local, ?MODULE}, ?MODULE, []).

start_child(Socket) ->
supervisor:start_child(?MODULE, [Socket]).

init([]) ->
Children = [
{"httpd_tcp_worker", {httpd_tcp_worker, start_link, []}, temporary, 5, worker, []}
],
{ok, {{simple_one_for_one, 0, 1}, Children}}.


また httpd_sup.erl の init([]) を編集し、追加実装した部分の起動処理を加えます。


src/httpd_sup.erl

%% ...(省略)...

init([]) ->
Children = [
{"httpd_tcp_listener", {httpd_tcp_listener, start_link, []}, permanent, 5, worker, []},
{"httpd_tcp_worker_sup", {httpd_tcp_worker_sup, start_link, []}, permanent, 5, supervisor, []}
],
{ok, { {one_for_all, 0, 1}, Children} }.


動作確認

アプリケーションの実装が終わったので起動してみます。

../rebar3 compile && erl -pa _build/default/lib/*/ebin -eval 'application:start(httpd).' -noshell

ブラウザからアクセスするとhello worldと表示されました。

hello_world.png


まとめ

Erlangでネットワーク・サーバを実装するのは難しくない印象です。

ただsupervisorを理解して使いこなすのはある程度の経験が必要そうです。