Edited at

Protocol BuffersをElixirで使うためにメジャーどころのプラグインを実装・比較してみた!(protobuf-elixir / exprotobuf)


はじめに

この記事はミクシィグループ 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の構成

以下の構成で検証していきます。


user_list.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の検証


セットアップ


プラグインの追加


mix.exs

defp deps do

[
{:protobuf, "~> 0.5.3"},
{:google_protos, "~> 0.1"}
]
end


protoコマンドのインストール

protobuf-elixirの場合Elixirファイルを生成する必要があるのでprotoコマンドをインストールします。

Macの場合以下のコマンドでインストール


shell

$ brew install protobuf


以下のコマンドでprotoファイルから.exファイルをジェネレートします。


shell

$ protoc --elixir_out=./ user_list.proto


すると以下のようなファイルが自動生成されます。


user_list.pb.ex

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ファイルを読み込んで使います。


セットアップ


プラグインの追加


mix.exs

def application do

[
applications: [:exprotobuf]
]
end

defp deps do
[
{:exprotobuf, "~> 1.2.9"}
]
end



protoファイルを読み込むmoduleの作成

以下のように使いたいprotoファイルをmoduleで読み込みます。


protobuf.ex

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に関してはファイルの自動生成も必要ないのでチーム開発でも問題なく導入できそうです!

以上になります。最後まで読んでいただきありがとう御座いました!