はじめに
この記事はミクシィグループ Advent Calendar 2018 17日の記事です。
https://qiita.com/advent-calendar/2018/mixi
最近Elixirを触り始めました。Elixir楽しい!
この記事はなに?
ElixirでProtocol Buffersを扱うプラグインが複数あったので、メジャーどころを纏めました!
Protocol Buffersについては下記の記事がわかりやすいです!
今さらProtocol Buffersと、手に馴染む道具の話
環境
今回の記事は以下の環境で検証しています。
- MacOS HighSierra
- Erlang 9.3.1
- Elixir 1.6.5
- Phoenix 1.4.0
比較するプラグイン
今回は以下のメジャーどころのプラグインを比較します。
exprotobuf
protobuf-elixir
題材とするprotoの構成
以下の構成で検証していきます。
syntax = "proto3";
package protobuf;
message UserList {
repeated User user_list = 1;
message User {
uint64 id = 1;
string name = 2;
Address address = 3;
message Address {
string addr1 = 1;
string addr2 = 2;
}
}
}
protobuf-elixirの検証
セットアップ
プラグインの追加
defp deps do
[
{:protobuf, "~> 0.5.3"},
{:google_protos, "~> 0.1"}
]
end
protoコマンドのインストール
protobuf-elixirの場合Elixirファイルを生成する必要があるのでprotoコマンドをインストールします。
Macの場合以下のコマンドでインストール
$ brew install protobuf
以下のコマンドでprotoファイルから.exファイルをジェネレートします。
$ protoc --elixir_out=./ user_list.proto
すると以下のようなファイルが自動生成されます。
defmodule Protobuf.UserList do
@moduledoc false
use Protobuf, syntax: :proto3
@type t :: %__MODULE__{
user_list: [Protobuf.UserList.User.t()]
}
defstruct [:user_list]
field :user_list, 1, repeated: true, type: Protobuf.UserList.User
end
defmodule Protobuf.UserList.User do
@moduledoc false
use Protobuf, syntax: :proto3
@type t :: %__MODULE__{
id: non_neg_integer,
name: String.t(),
address: Protobuf.UserList.User.Address.t()
}
defstruct [:id, :name, :address]
field :id, 1, type: :uint64
field :name, 2, type: :string
field :address, 3, type: Protobuf.UserList.User.Address
end
defmodule Protobuf.UserList.User.Address do
@moduledoc false
use Protobuf, syntax: :proto3
@type t :: %__MODULE__{
addr1: String.t(),
addr2: String.t()
}
defstruct [:addr1, :addr2]
field :addr1, 1, type: :string
field :addr2, 2, type: :string
end
上記の自動生成ファイルを使ってencode, decodeを行います。
encode/decodeしてみる
encode
encodeを行うには各Module毎にnew
メソッドを使って値を入れていきます。
user_list = [
# Userに対し値を入れていく
Protobuf.UserList.User.new(
id: 1,
name: "ヤマダ",
# AddressはUserの入れ子なので、ここでもnewを宣言する。
address:
Protobuf.UserList.User.Address.new(
addr1: "東京都千代田区",
addr2: "0-0-0"
)
),
Protobuf.UserList.User.new(
id: 2,
name: "タナカ",
address:
Protobuf.UserList.User.Address.new(
addr1: "東京都渋谷区",
addr2: "1-1-1"
)
)
]
# トップレベルは配列なのでのUserListに上記で作成した、Userの配列を入れる。
user = Protobuf.UserList.new(user_list: user_list)
# binaryに変換
user_binary = Protobuf.UserList.encode(user) # <<10, 45, 8, 1, 18, 9, 227, 131, 164, 227, 131, 158, ...>>
上記のように各Moduleに対して値を入れていきます。
それらを encode
に入れることで、バイナリへと変換することが出来ます。
decode
decoodeは先程作成したバイナリをそのままdecode
を使います。
# 先程作成したbinaryをdecodeに入れる
Protobuf.UserList.decode(user_binary)
上記のように要素のトップレベルのModuleにbinaryを入れることで、復元することが出来ます。
所感
思ったよりも簡単に実装することが出来ました!ただ、protobuf-elixirの場合、
手元の環境でElixir用に自動生成ファイルを作成する必要があるので、
チーム開発をおこなう際にメンバーの環境だったり、
生成したファイルの管理だったりとそこら辺の管理をちゃんと気にしてあげる必要があるなと感じました。
exprotobufの検証
exprotobufはprotoファイルを読み込んで自動生成ファイルを作成する必要がありません。
Elixir上に直接protoファイルを読み込んで使います。
セットアップ
プラグインの追加
def application do
[
applications: [:exprotobuf]
]
end
defp deps do
[
{:exprotobuf, "~> 1.2.9"}
]
end
protoファイルを読み込むmoduleの作成
以下のように使いたいprotoファイルをmoduleで読み込みます。
defmodule Protobuf do
use Protobuf, from: Path.expand("./user_list.proto", __DIR__)
end
encode/decodeしてみる
encode
ここら先はprotobuf-elixir
と同じ手順です。
encodeを行うには各Module毎にnew
メソッドを使って値を入れていきます。
user_list = [
# Userに対し値を入れていく
Protobuf.UserList.User.new(
id: 1,
name: "ヤマダ",
# AddressはUserの入れ子なので、ここでもnewを宣言する。
address:
Protobuf.UserList.User.Address.new(
addr1: "東京都千代田区",
addr2: "0-0-0"
)
),
Protobuf.UserList.User.new(
id: 2,
name: "タナカ",
address:
Protobuf.UserList.User.Address.new(
addr1: "東京都渋谷区",
addr2: "1-1-1"
)
)
]
# トップレベルは配列なのでのUserListに上記で作成した、Userの配列を入れる。
user = Protobuf.UserList.new(user_list: user_list)
# binaryに変換
user_binary = Protobuf.UserList.encode(user) # <<10, 45, 8, 1, 18, 9, 227, 131, 164, 227, 131, 158, ...>>
上記のように各Moduleに対して値を入れていきます。
それらを encode
に入れることで、バイナリへと変換することが出来ます。
decode
こちらもprotobuf-elixir
と同じ手順です。
# 先程作成したbinaryをdecodeに入れる
Protobuf.UserList.decode(user_binary)
上記のように要素のトップレベルのModuleにbinaryを入れることで、復元することが出来ます。
所感
protobuf-elixir同様こちらも思ったより簡単に実装することが出来ました!
しかも、exprotobufの場合はファイルを自動生成せず、protoを直接読み込むことが出来るので、
チーム開発においてもそれほど障壁なく導入することが出来そうだなと感じました!
まとめ
ElixirでProtocol Buffersを使う際に使うプラグインのメジャーどころを比較してみました!
両プラグイン共学習コストも低くサクッと導入することが出来てとても良かったです!
特に、exprotobuf
に関してはファイルの自動生成も必要ないのでチーム開発でも問題なく導入できそうです!
以上になります。最後まで読んでいただきありがとう御座いました!