カウボーイWebサーバー

こんにちは

このチュートリアルでは、Cowboy Webサーバーに慣れていない人にその使用方法を紹介する予定です。 彼との経験がある人にとっては、このチュートリアルはおもしろくないでしょうが、伝聞だけでカウボーイについて知っている人にとっては-ようこそ!



私たちがすること:

  1. サーバーの最も簡単なインストールと起動
  2. ルーティングの概要、静的メンテナンス
  3. ErlyDTL(ErlangのDjangoテンプレート言語)によるパターン化




便宜上、鉄筋、簡単なインストールが必要です。

> git clone git://github.com/basho/rebar.git && cd rebar && ./bootstrap
      
      





これで、rebar実行可能ファイルがディレクトリに表示されました-$ PATHのどこかにコピー(またはより良いリンク)します。 例:

 > sudo ln -s `pwd`/rebar /usr/bin/rebar
      
      







そしてここに行きます!



サーバーの最も簡単なインストールと起動



最初に、将来のアプリケーションのためにディレクトリとスケルトンを作成します。鉄筋がこれを助けてくれます。 アプリケーションを作成する場所に行き、次のコマンドを実行します。

 > mkdir webserver && cd webserver && rebar create-app appid=webserver
      
      





rebar create-app appid=webserver



は、単純なErlangアプリケーションのスケルトンを作成します。これで、webserverディレクトリは次のようになります。



次に行うことは、Cowboy、Sync、Mimetypes、およびErlydtlへの依存関係を追加することです。 CowboyはWebサーバーです。Syncは、変更のたびにサーバーを再起動せずに、更新中に変更されたモジュールを再コンパイルできるユーティリティです。Mimetypesは、拡張子がmimetypeと一致するかどうかを判断するためのライブラリです(静的で作業している場合に便利です)。 Erlydtl-テンプレートエンジン。 rebar.configというrebarの構成ファイルを作成します。

Rebar.configコンテンツ
 {deps, [ {cowboy, ".*", {git, "https://github.com/extend/cowboy.git", {branch, "master"}}}, {sync, ".*", {git, "git://github.com/rustyio/sync.git", {branch, "master"}}}, {mimetypes, ".*", {git, "git://github.com/spawngrid/mimetypes.git", {branch, "master"}}}, {erlydtl, ".*", {git, "git://github.com/evanmiller/erlydtl.git", {branch, "master"}}} ]}.
      
      







src / webserver.erlファイルを作成してみましょう。ここでは、サーバーを今すぐ開始および停止します。

Src / webserver.erlコンテンツ
 -module(webserver). %% API -export([ start/0, stop/0 ]). -define(APPS, [crypto, ranch, cowboy, webserver]). %% =================================================================== %% API functions %% =================================================================== start() -> ok = ensure_started(?APPS), ok = sync:go(). stop() -> sync:stop(), ok = stop_apps(lists:reverse(?APPS)). %% =================================================================== %% Internal functions %% =================================================================== ensure_started([]) -> ok; ensure_started([App | Apps]) -> case application:start(App) of ok -> ensure_started(Apps); {error, {already_started, App}} -> ensure_started(Apps) end. stop_apps([]) -> ok; stop_apps([App | Apps]) -> application:stop(App), stop_apps(Apps).
      
      







webserver:start()の呼び出しにより、暗号化、牧場、カウボーイ、ウェブサーバー、およびSyncを使用した自動更新が順番に開始され、webserver:stopがすべての逆の順序で実行を停止します。

カスケードの準備ができました。次はカウボーイに移動します。 webserver_app.erlを開き、start / 2関数を編集します。

関数webserver_app:start / 2
 start(_StartType, _StartArgs) -> Dispatch = cowboy_router:compile([ {'_', [ {"/", index_handler, []}, {'_', notfound_handler, []} ]} ]), Port = 8008, {ok, _} = cowboy:start_http(http_listener, 100, [{port, Port}], [{env, [{dispatch, Dispatch}]}] ), webserver_sup:start_link().
      
      







ディスパッチルールでは、notfound_handlerを使用してサーバーに到着する「/」以外のすべてのリクエストを処理し(404エラーを返します)、index_handlerを使用して「/」へのリクエストを処理します。 したがって、それらを作成する価値があります。

Src / index_handler.erlコンテンツ
 -module(index_handler). -behaviour(cowboy_http_handler). %% Cowboy_http_handler callbacks -export([ init/3, handle/2, terminate/3 ]). init({tcp, http}, Req, _Opts) -> {ok, Req, undefined_state}. handle(Req, State) -> Body = <<"<h1>It works!</h1>">>, {ok, Req2} = cowboy_req:reply(200, [], Body, Req), {ok, Req2, State}. terminate(_Reason, _Req, _State) -> ok.
      
      







Src / notfound_handler.erlコンテンツ
 -module(notfound_handler). -behaviour(cowboy_http_handler). %% Cowboy_http_handler callbacks -export([ init/3, handle/2, terminate/3 ]). init({tcp, http}, Req, _Opts) -> {ok, Req, undefined_state}. handle(Req, State) -> Body = <<"<h1>404 Page Not Found</h1>">>, {ok, Req2} = cowboy_req:reply(404, [], Body, Req), {ok, Req2, State}. terminate(_Reason, _Req, _State) -> ok.
      
      







それだけです-localhost :8008およびlocalhost :8008 / WHATEVERのリクエストを処理できるシンプルなWebサーバーを作成しました。 これで、Webサーバーをコンパイルして起動することができます。

 > rebar get-deps > rebar compile > erl -pa ebin deps/*/ebin -s webserver
      
      





rebar get-deps



は構成から依存関係をrebar get-deps



rebar compile



erl -pa ebin deps/*/ebin -s webserver



コードをrebar compile



erl -pa ebin deps/*/ebin -s webserver



はサーバー自体を起動します。 ところで、上記の操作の実行を容易にするために、簡単なMakefileを作成します。

メイクファイルのコンテンツ
 REBAR = `which rebar`

 all:depsコンパイル

深さ:
	 @($(REBAR)get-deps)

コンパイル:クリーン
	 @($(REBAR)コンパイル)

きれいな:
	 @($(REBAR)クリーン)

実行:
	 @(erl -pa ebin deps / * / ebin -s webserver)

 .PHONY:すべての依存関係はクリーンランをコンパイルします




これで、 make



呼び出しでプロジェクトをコンパイルし、 make run



呼び出しで開始make run



ます

サーバーが起動したら、最初にlocalhost :8008に移動し、次にlocalhost :8008 /に移動して、サーバーが期待どおりに動作していることを確認し、最初の要求に「It works」、2番目に「404 Page Not Found」



ルーティングの概要、静的メンテナンス



カウボーイでのルーティングは、それが最も便利であるとは言えませんが、かなり許容範囲があります。URLにパラメーターを渡し、これらのパラメーターを検証するなどの主な機能が利用可能です。 これまでのところ、ディスパッチルールには2つのルートしかありません。

 {"/", index_handler, []}, {'_', notfound_handler, []}
      
      





これは別の内部にあり、ネストするホストを決定します。 これとルーティングの一般的な詳細についてはこちらをご覧ください: github.com/extend/cowboy/blob/master/guide/routing.mdそしてここでは、アトム「_」はルートが完全にすべてのアドレスへのリクエストに一致することを意味することだけを明確にします、notfound_handlerはリクエストを処理するモジュールの名前、[]はextのリストです。 モジュールに渡されるパラメーター

静的変数をpriv / css priv / js、priv / imgサブディレクトリのprivディレクトリに保存し、次のルールに従って照合します。

 /css/WHATEVER -> /priv/css/WHATEVER /js/WHATEVER -> /priv/js/WHATEVER /img/WHATEVER -> priv/img/WHATEVER
      
      





これを行うには、それぞれ3つのルートを追加します。

 Dispatch = cowboy_router:compile([ {'_', [ {"/css/[...]", cowboy_static, [ {directory, {priv_dir, webserver, [<<"css">>]}}, {mimetypes, {fun mimetypes:path_to_mimes/2, default}} ]}, {"/js/[...]", cowboy_static, [ {directory, {priv_dir, webserver, [<<"js">>]}}, {mimetypes, {fun mimetypes:path_to_mimes/2, default}} ]}, {"/img/[...]", cowboy_static, [ {directory, {priv_dir, webserver, [<<"img">>]}}, {mimetypes, {fun mimetypes:path_to_mimes/2, default}} ]}, {"/", index_handler, []}, {'_', notfound_handler, []} ]} ]).
      
      





mimetypes:path_to_mimes / 2関数は、ファイル拡張子によって正しいmimetypeを返す役割を果たします。

前の3つのルートは、わずかな例外を除いてほぼ完全に相互にコピーしていることを確認するのは簡単です。静的ルートの生成を関数に入れて、ルートを置き換えましょう。

 Static = fun(Filetype) -> {lists:append(["/", Filetype, "/[...]"]), cowboy_static, [ {directory, {priv_dir, webserver, [list_to_binary(Filetype)]}}, {mimetypes, {fun mimetypes:path_to_mimes/2, default}} ]} end, Dispatch = cowboy_router:compile([ {'_', [ Static("css"), Static("js"), Static("img"), {"/", index_handler, []}, {'_', notfound_handler, []} ]} ]).
      
      





ここで、新しいディスパッチルールを有効にするには、サーバーを再起動するか、カウボーイ関数を使用する必要があります。set_env/ 3

最初のものはスポーツマンに似ていないため、ルーティングルールで全員のサーバーを再起動することに苦しんでいるので、webserverファイルのルーティングを更新する関数を追加して、webserver:update_routing()がコンソールで呼び出されるようにします。 そして、webserver:update_routing / 0関数が新しいルートを認識するように、別の関数でそれらの定義を取り出します。 その結果、webserver_app.erlファイルは次の形式を取ります。

Src / webserver_app.erlコンテンツ
 -module(webserver_app). -behaviour(application). %% Application callbacks -export([ start/2, stop/1 ]). %% API -export([dispatch_rules/0]). %% =================================================================== %% API functions %% =================================================================== dispatch_rules() -> Static = fun(Filetype) -> {lists:append(["/", Filetype, "/[...]"]), cowboy_static, [ {directory, {priv_dir, webserver, [list_to_binary(Filetype)]}}, {mimetypes, {fun mimetypes:path_to_mimes/2, default}} ]} end, cowboy_router:compile([ {'_', [ Static("css"), Static("js"), Static("img"), {"/", index_handler, []}, {'_', notfound_handler, []} ]} ]). %% =================================================================== %% Application callbacks %% =================================================================== start(_StartType, _StartArgs) -> Dispatch = dispatch_rules(), Port = 8008, {ok, _} = cowboy:start_http(http_listener, 100, [{port, Port}], [{env, [{dispatch, Dispatch}]}] ), webserver_sup:start_link(). stop(_State) -> ok.
      
      







次に、update_routing関数をwebserver.erlモジュールに追加します。

Webサーバー関数:update_routes / 0
 update_routes() -> Routes = webserver_app:dispatch_rules(), cowboy:set_env(http_listener, dispatch, Routes).
      
      







また、関数を-export()属性に追加することを忘れないでください。その後、次のようになります。

 %% API -export([ start/0, stop/0, update_routes/0 ]).
      
      





コンソールでwebserver:update_routes().



を実行しますwebserver:update_routes().



静的なディレクトリを作成する

 > mkdir priv && cd priv && mkdir css js img
      
      





そしてそこにいくつかの適切なファイルを置き、その後、それらが期待どおりにローカルホストに与えられていることを確認できます:8008 / PATH / FILE



ErlyDTL(Erlang用Djangoテンプレート言語)を使用したパターニング



Erlangの悪名高いChicago Boss Webフレームワークの作者であるEvan Millerは、Django Template Language(https://docs.djangoproject.com/en/dev/topics/templates/)をErlangに移植しました。 実際、将来のプロジェクトで使用することをお勧めするのはこのテンプレートエンジンです。これ以上優れた選択肢はまだありません。

新しいディレクトリwebserver / tplを作成し、そこに3つのテンプレートを保存します。



Tpl / layout.dtlコンテンツ
 <!DOCTYPE html> <html> <head> <title>Webserver</title> </head> <body> {% block content %}{% endblock %} </body> </html>
      
      







Tpl / index.dtlコンテンツ
 {% extends "layout.dtl" %} {% block content %} <h1>Hello, {{ username | default : "stranger" }}!</h1> {% endblock %}
      
      







Tpl / 404.dtlコンテンツ
 {% extends "layout.dtl" %} {% block content %} <h1>URL <span style="color:red;">{{ url }}</span> does not exists.</h1> {% endblock %}
      
      









テンプレートを使用するには、テンプレートをコンパイルする必要があります。 これは、次のようにerlydtl:compile / 3を使用して行われます。

 ok = erlydtl:compile("tpl/layout.dtl", "layout_tpl", []), ok = erlydtl:compile("tpl/index.dtl", "index_tpl", []), ok = erlydtl:compile("tpl/404.dtl", "404_tpl", []).
      
      





最後の引数は、テンプレートをコンパイルするためのオプションのリストです。詳細については、 github.com / evanmiller / erlydtlを参照してください。

変更するたびにすべてのテンプレートを手動でコンパイルしないように、再コンパイルする関数をwebserverモジュールに作成します。

テンプレートを再コンパイルするための関数
 c_tpl() -> c_tpl([]). c_tpl(Opts) -> c_tpl(filelib:wildcard("tpl/*.dtl"), Opts). c_tpl([], _Opts) -> ok; c_tpl([File | Files], Opts) -> ok = erlydtl:compile(File, re:replace(filename:basename(File), ".dtl", "_tpl", [global, {return, list}]), Opts), c_tpl(Files, Opts).
      
      







それらをエクスポートします。

 %% API -export([ start/0, stop/0, update_routes/0, c_tpl/0, c_tpl/1, c_tpl/2 ]).
      
      





c_tpl / 0はオプションなしでtplディレクトリからすべてのテンプレートを再コンパイルし、c_tpl / 1は指定されたオプションで同じことを行い、c_tpl / 2は指定されたオプションで指定されたファイルを再コンパイルします。 Erlangのコンソールでwebserver:c_tpl().



実行して、すべてのテンプレートをコンパイルしましょうwebserver:c_tpl().





また、rebar.configを更新して、コンパイル時にテンプレートもコンパイルするようにします( egobrainヒントをありがとう)。

更新されたrebar.config
 {plugins,[rebar_erlydtl_compiler]}. {deps, [ {cowboy, ".*", {git, "https://github.com/extend/cowboy.git", {branch, "master"}}}, {sync, ".*", {git, "git://github.com/rustyio/sync.git", {branch, "master"}}}, {mimetypes, ".*", {git, "git://github.com/spawngrid/mimetypes.git", {branch, "master"}}}, {erlydtl, ".*", {git, "git://github.com/evanmiller/erlydtl.git", {branch, "master"}}} ]}. {erlydtl_opts,[ {compiler_options, [debug_info]}, [ {doc_root, "tpl"}, {out_dir, "ebin"}, {source_ext, ".dtl"}, {module_ext, "_tpl"} ] ]}.
      
      







残念なことに、テンプレートの変更を簡単な方法でキャッチするためにSyncを取得することはできませんでした。少し後でコードを調べますので、モジュールで再コンパイルするための関数を残しました。



次に、コンパイル済みのテンプレートを返すようにハンドラーを編集し、必要な変数をテンプレートに転送します。



Src / index_handler.erlコンテンツ
 -module(index_handler). -behaviour(cowboy_http_handler). %% Cowboy_http_handler callbacks -export([ init/3, handle/2, terminate/3 ]). init({tcp, http}, Req, _Opts) -> {ok, Req, undefined_state}. handle(Req, State) -> {Username, Req2} = cowboy_req:qs_val(<<"username">>, Req, "stranger"), {ok, HTML} = index_tpl:render([{username, Username}]), {ok, Req3} = cowboy_req:reply(200, [], HTML, Req2), {ok, Req3, State}. terminate(_Reason, _Req, _State) -> ok.
      
      







Src / notfound_handler.erlコンテンツ
 -module(notfound_handler). -behaviour(cowboy_http_handler). %% Cowboy_http_handler callbacks -export([ init/3, handle/2, terminate/3 ]). init({tcp, http}, Req, _Opts) -> {ok, Req, undefined_state}. handle(Req, State) -> {URL, Req2} = cowboy_req:url(Req), {ok, HTML} = '404_tpl':render([{url, URL}]), {ok, Req3} = cowboy_req:reply(404, [], HTML, Req2), {ok, Req3, State}. terminate(_Reason, _Req, _State) -> ok.
      
      







実際、それがすべてです。 localhost :8008 /?を開き、ユーザー名= worldまたはlocalhost :8008 / qweqweasdasdを実行し、すべてが期待どおりに機能することを喜んでください。



完全なプロジェクトコードは、 github.com / chvanikoff / webserverにあります。



これで私の話は終わりです。次の記事では、今日作成したアプリケーションに多言語サポートを追加する方法について説明します。 質問、コメント、コメントを歓迎します;)



All Articles