Elixir & Phoenix framework の始め方
次世代システム研究室の データストア 好きの Y.I. です。
今回はデータストアから離れて、プログラミング言語 Elixir と Phoenix Framework についてまとめたいと思います。すでに Elixir の記事 「Elixir & Phoenix は意外と広告向けサーバに向いてる?」、 「GraphQL Relay 対応サーバを Elixir で作る」、「VR環境に構築したプレゼンテーションルーム」 がありますが、始め方はあった方が良いだろうということで、今回は Elixir と Phoenix Framework の特徴や学び方、そして PostgreSQL を使った CRUD Web アプリケーションを作るところまでまとめたいと思います。
■ Elixir 特徴
- Rubyの文法に似た関数型言語
- Rails のコミッター José Valim 氏が 主要コミッター
- 2012年に登場
- 30年以上の歴史がある ErlangVM 上で動作
- ネットワークが繋がるノード間で容易にプロセス間通信可能
- スケールアウトしやすい
- Elixir で Erlang 言語の代わりに ErlangVM アプリケーションを作成できる
- Erlang eco system を利用可能
- 軽量スレッド(Erlangでは軽量プロセスと呼ばれる)
- デフォルト設定で1ノードに26万プロセスまで起動可能
- WhatsApp では1ノードで200万を達成(over 2 million tcp connections)
- Full GC (Stop the world) がない
- 軽量プロセス毎の GC のため他プロセスに影響しない
- Actor モデル
- 軽量プロセス間の連携はメッセージ送受信による実施
- 疎結合
- プロセス間通信に利用できる
- Supervisor による信頼性向上
- 例外発生時は crush させて自動リトライなど事前に定めた戦略に沿ってリカバー可能
- マクロを使って独自の文法がつくれる
- ビルドツール mix がある
- Clojure の Leiningen や Scala の sbt のようなビルドツール
- REPL iex でインタラクティブに開発可能
■ Phoenix Framework 特徴
- Web フレームワーク
- Ruby on Rails に似たフルスタックフレームワーク
- Chris McCord 氏や Rails のコミッター José Valim 氏が 主要コミッター
- Ruby on Rails よりも処理速度が速い
- こちらの記事だとスループット13倍: github mroth/phoenix-showdown
- 同記事の3回目の比較結果 9倍 :github mroth/phoenix-showdown Round 3
- Scaffold によりコード雛形生成
- ActiveRecord のような Ecto
- WebSocketサーバを簡単に実現可能
■ 学び方
▼ Elixir
Elixirの学び方は、やはり公式サイトが一番です。公式サイトの GUIDES メニューを中心に文法や各種機能を学べます。公式サイトは英語ですが日本語訳を公開してくれているサイトもあります。Elixir 公式サイト:http://elixir-lang.org/
Elixir 公式サイト GUIDES メニュー:http://elixir-lang.org/getting-started/introduction.html
Elixir 公式サイト日本語訳:http://elixir-ja.sena-net.works/
Elixir School サイトもおすすめです。Twitter社の Scalar School にインスパイアされたサイトです。
Elixir School:https://elixirschool.com/
Elixir School 日本語訳:https://elixirschool.com/jp/
チートシートもおさえておくと記号や関数などすぐに分かるので便利です。
チートシート:https://media.pragprog.com/titles/elixir/ElixirCheat.pdf
こちらは「elixir cheatsheet」で検索して見つけたクイックリファレンスです。図やコードを多用した分かりやすいサイトです。
github itsgreggreg/elixir_quick_referencehttps://github.com/itsgreggreg/elixir_quick_reference/blob/master/README.md
書籍について、
Elixir では「プログラミング Elixir / Dave Thomas著」 が最初の日本語 Elixir 本になると思います。翻訳本独特の言い回しがありますがおすすめです。
Elixir に慣れてきたら Erlang についても学んだほうが有利です。 Elixir から Erlang コードをシームレスに実行できますし、Elixir のカンファレンスなどに参加するとカジュアルに Erlang 周りに話が出てくることがあります。
「すごい Erlang ゆかいに学ぼう!」:https://estore.ohmsha.co.jp/titles/978427406912P こちらの本では Erlang 全般の他、 Elixir 本に比べて Supervisor、 Actor モデルや OTP などの理解が深まります。
▼ Phoenix Framework
Phoenix Framework では、Phoenix Framework 公式サイト
「Programming Phoenix: Productive |> Reliable |> Fast」がおすすめです。日本語訳はまだ出版されていませんが読みやすい英語で書かれていると思います。
■ Code Editor
Elixir をサポートしているエディターは複数あり開発しやすい環境は整っています。公式サイトの情報を一部ですがご紹介すると
Emacs Mode
Vim Elixir
Sublime Plugin
Atom Package
Intellij Elixir
Visual Studio Elixir
などがあります。私は Atom editor で開発しています。
■ 独特(?)なお作法
個人的に分かりづらかった事を紹介します。|> (パイプライン演算子)
処理の連結。式の結果を次の式の第一パラメータとして渡す。Elixirで一番特徴的な演算子。conn |> f1(a) |> f2(b) # conn を評価して次の式に渡す、 次の式ではf1(conn, a)として評価して次の式に渡す、 次の式ではf2(f1(conn, a)のresult, b)として評価される
/n (スラッシュ 数値)
パラメータ数を表します。Elixirを始めて暫くの間何を表しているのか分かりませんでした。。
is_atom/1 # パラメータが1つである is_atom function のこと is_atom/2 # パラメータが2つである is_atom function のこと
Atom (アトム)
定義した名前がそのまま値となるコンスタント値。定数やMapのKeyとしてよく利用されます。文字列とは異なります。iex(1)> :a :a iex(2)> :a == "a" false # atomのaと文字列のaを比較するとfalseとなります。
Tuple(タプル)
配列やListに近くあらゆる型の値を保持できます。各要素を連続してメモリに保持するのでIndex指定アクセスやSizeの取得がListに比べて高速です。要素の追加など変更は全ての要素をコピーして新しいTupleを作成するので低速です。Atomの:ok/:ngを先頭要素にした形でFunctionの戻り値としてよく利用されます。iex(1)> tuple = {:ok, "hello"} {:ok, "hello"} iex(2)> tuple_size(tuple) 2説明はこのくらいにして、 Elixir と Phoenix Framework で PostgreSQL への CRUD Webアプリケーションを作成してみましょう。
■ CRUD アプリケーションの作成
Phoenix 公式サイトを参考にします。http://www.phoenixframework.org/docs/ecto-models
今回作成する DB テーブルはこちらになります。データベースやテーブルの作成は Phoenix Framework の ecto を使って作成します。直接 PostgreSQL を操作するのは Create User のみです。
CREATE TABLE users ( id integer NOT NULL, name character varying(255), email character varying(255), bio character varying(255), number_of_pets integer, inserted_at timestamp without time zone NOT NULL, updated_at timestamp without time zone NOT NULL );事前に DB へユーザを作成しておきましょう。
CREATE USER postgres; ALTER USER postgres PASSWORD 'postgres'; ALTER USER postgres WITH SUPERUSER;では、CRUD アプリケーションを作成していきましょう。
プロジェクト「phoenix_ecto_sample」の作成
$ mix phoenix.new phoenix_ecto_sample
* creating phoenix_ecto_sample/config/config.exs * creating phoenix_ecto_sample/config/dev.exs ・・・(省略) * creating phoenix_ecto_sample/web/views/page_view.ex Fetch and install dependencies? [Yn] Y * running mix deps.get * running npm install && node node_modules/brunch/bin/brunch build We are all set! Run your Phoenix application: $ cd phoenix_ecto_sample $ mix phoenix.server You can also run your app inside IEx (Interactive Elixir) as: $ iex -S mix phoenix.server Before moving on, configure your database in config/dev.exs and run: $ mix ecto.create作成されたPJへ移動します
$ cd phoenix_ecto_sampleconfig ファイルの DB 接続定義を確認しましょう
$ vim config/dev.exs
# Configure your database config :phoenix_ecto_sample, PhoenixEctoSample.Repo, adapter: Ecto.Adapters.Postgres, username: "postgres", password: "postgres", database: "phoenix_ecto_sample_dev", hostname: "localhost", pool_size: 10 ※ DB 定義初期値(->上記の定義を変更する事で作成するデータベース名やユーザを変更できます) ※ user/pass [postgres] ※ database [phoenix_ecto_sample_dev] mix phoenix.new で指定したプロジェクト名(dev環境は末尾が_dev)になります。では DB を 作成します
$ mix ecto.create
==> connection Compiling 1 file (.ex) Generated connection app ・・・(省略)・・・ Generated phoenix_ecto_sample app The database for PhoenixEctoSample.Repo has been createdDB 作成結果を psql コマンドで PostgreSQL へ接続して確認してみます。
$ psql -U postgres postgres=# \l
List of databases Name | Owner | Encoding | Collate | Ctype | Access privileges --------------------------+------------+----------+---------+-------+--------------------------- phoenix_ecto_sample_dev | postgres | UTF8 | C | C | ※ phoenix_ecto_sample_dev データベースが作成されています次に users テーブル用の CRUD コードの作成(Scaffold)を行います
$ mix phoenix.gen.html User users name:string email:string bio:string number_of_pets:integer以下のコードが作成されます。これだけで users テーブルに対する CRUD コードが作成されます。
* creating web/controllers/user_controller.ex * creating web/templates/user/edit.html.eex * creating web/templates/user/form.html.eex * creating web/templates/user/index.html.eex * creating web/templates/user/new.html.eex * creating web/templates/user/show.html.eex * creating web/views/user_view.ex * creating test/controllers/user_controller_test.exs * creating web/models/user.ex * creating test/models/user_test.exs * creating priv/repo/migrations/20170330062028_create_user.exs Add the resource to your browser scope in web/router.ex: resources "/users", UserController ※ こちら↑の通りに後ほど router.ex ファイルに resources ... を追加します。 Remember to update your repository by running migrations: $ mix ecto.migrateテーブル作成に使用される web/models/user.ex を確認してみましょう。
defmodule PhoenixEctoSample.User do use PhoenixEctoSample.Web, :model schema "users" do field :name, :string field :email, :string field :bio, :string field :number_of_pets, :integer timestamps() end @doc """ Builds a changeset based on the `struct` and `params`. """ def changeset(struct, params \\ %{}) do struct |> cast(params, [:name, :email, :bio, :number_of_pets]) |> validate_required([:name, :email, :bio, :number_of_pets]) end end ※ schema "users" do の後に mix phoenix.gen.html コマンドで指定した テーブル名やカラムが定義されています。 ※ timestamps() はマクロで inserted_at/updated_at カラムを追加します。router.ex に HTTP アクセスエンドポイントを追加します。
$ vim web/router.ex
defmodule PhoenixEctoSample.Router do use PhoenixEctoSample.Web, :router pipeline :browser do plug :accepts, ["html"] plug :fetch_session plug :fetch_flash plug :protect_from_forgery plug :put_secure_browser_headers end pipeline :api do plug :accepts, ["json"] end scope "/", PhoenixEctoSample do pipe_through :browser # Use the default browser stack get "/", PageController, :index ※ 追加 resources "/users", UserController end # Other scopes may use custom stacks. # scope "/api", PhoenixEctoSample do # pipe_through :api # end end ※ [resources "/users", UserController]を追加しました。 ※ resources は GET/POST/PUT等のメソッドを追加します。追加したエンドポイントを確認してみましょう。
$ mix phoenix.routes ※ このコマンドでエンドポイントが全て表示されます(便利ですね)
Compiling 8 files (.ex) page_path GET / PhoenixEctoSample.PageController :index user_path GET /users PhoenixEctoSample.UserController :index user_path GET /users/:id/edit PhoenixEctoSample.UserController :edit user_path GET /users/new PhoenixEctoSample.UserController :new user_path GET /users/:id PhoenixEctoSample.UserController :show user_path POST /users PhoenixEctoSample.UserController :create user_path PATCH /users/:id PhoenixEctoSample.UserController :update PUT /users/:id PhoenixEctoSample.UserController :update user_path DELETE /users/:id PhoenixEctoSample.UserController :delete ※ user_path となっている8つのエンドポイントが resources により追加されました。次に users テーブルを ecto で作成しましょう。
$ mix ecto.migrate
15:54:56.283 [info] == Running PhoenixEctoSample.Repo.Migrations.CreateUser.change/0 forward 15:54:56.283 [info] create table users 15:54:56.317 [info] == Migrated in 0.0s作成されたテーブルを psql コマンドで確認してみます。
$ psql -U postgres phoenix_ecto_sample_dev phoenix_ecto_sample_dev=# \d
List of relations Schema | Name | Type | Owner --------+-------------------+----------+---------- public | schema_migrations | table | postgres public | users | table | postgres public | users_id_seq | sequence | postgres (3 rows) ※ users テーブルや sequence などが作成されています。
phoenix_ecto_sample_dev=# \d users
Table "public.users" Column | Type | Modifiers ----------------+-----------------------------+---------------------------------------------------- id | integer | not null default nextval('users_id_seq'::regclass) name | character varying(255) | email | character varying(255) | bio | character varying(255) | number_of_pets | integer | inserted_at | timestamp without time zone | not null updated_at | timestamp without time zone | not null Indexes: "users_pkey" PRIMARY KEY, btree (id) ※ users テーブルに nameやemailなど指定したカラムが作成されています。ここまでの操作、router.ex への 1行の実装のみで CRUD Web アプリケーションが完成しました。
では、 Web アプリケーションを起動してみましょう。
$ mix phoenix.server
[info] Running PhoenixEctoSample.Endpoint with Cowboy using http://localhost:4000 30 Mar 15:59:50 - info: compiled 6 files into 2 files, copied 3 in 1.5 secアプリケーションが起動したのでブラウザでアクセスしてみましょう。
http://localhost:4000/users一覧画面が表示されます。
[New user]リンクからユーザ情報を作成してみましょう。
各項目を入力した状態です。
[Submit]ボタンをクリックして登録してみます。
一覧画面で情報が登録されていることが確認できます。
psql で DB を直接確認してもレコードが登録されています。
phoenix_ecto_sample_dev=# select * from users; id | name | email | bio | number_of_pets | inserted_at | updated_at ----+-----------+---------------+-----------+----------------+----------------------------+---------------------------- 1 | user name | [email protected] | biography | 1 | 2017-03-30 07:04:31.541726 | 2017-03-30 07:04:31.562391 (1 row)
■ 最後に
Elixir/Phoenix Framework を使って簡単に CRUD Web アプリケーションを作成出来る事が分かるかと思います。個人的な意見ですが、 Scala と比較すると Elixir の方が学習コストがかなり低いと感じます。 Ruby よりも処理速度が速くて、 ErlangVM の堅牢さの上で動作する事はメリットが大きいのではないでしょうか。Elixir/Phoenix Framework は登場して数年しか経過していませんが、確実に利用者が増えてきていて、本番サービスでの利用も多数出てきています。 2017/4/1に Elixir Conf Japan 2017 もありますし、日本語書籍も増えてきています。今年はさらに注目が高まることでしょう!
次回は Elixir/Phoenix で WebSocket アプリケーションを作成した話(VR環境に構築したプレゼンテーションルーム)についてまとめる予定です。
次世代システム研究室では、グループ全体のインテグレーションを支援してくれるアーキテクトを募集しています。アプリケーション開発の方、次世代システム研究室にご興味を持って頂ける方がいらっしゃいましたら、ぜひ 募集職種一覧 からご応募をお願いします。
皆さんのご応募をお待ちしています。