LoginSignup
4
3

More than 3 years have passed since last update.

Avro - Ruby でスキーマを作って C# 用のIDLを出力して Unity で利用する

Last updated at Posted at 2016-09-29

環境

  • OSX 10.11.5
  • Ruby 2.2.5
  • .Net 2.0 (Unity)
  • Unity 5.4
  • mono 4.4.1

目的

クライアント側でJSONのパーサやレコードクラスの定義を手で書くのを避ける。
JSON に含まれるデータの型を明示する。
(long のつもりが int になっててオーバーフローしてしまった、とかいう悲惨な事故を起こさない)

前提条件

本稿では avro-builder は rails アプリ から利用する事を前提とします。
また C# の IDL は Unity 開発で利用する事としています。

ツール選定

要件

  • データを JSON で扱える
  • クライアントでの型を明示できる
  • C# のランタイムがある
  • C# のレコードクラスを生成してくれる
  • Unity で使える
  • iOS / Android の IL2CPP ビルドで動作する
  • これによるサーバ側の作業を要さない

候補

  • Protocol Buffer
  • Avro

次点で下記も候補に上がったが、IDL 生成までは公式ではサポートされていない。

  • Msgpack
  • NetSerializer

結果

Avro

選定理由

Avro

対応する言語が潤沢で、ツールも充実していた。
JIT コンパイルしている箇所があったが、局所的なので修正して使えそうだった。

Protocol Buffer

今回の開発環境が Unity でなければ採用したかった。
公式のC#バインディングが Unity 対応していない。
非公式のバインディングでは protobuf-net が存在するが、更新状況が芳しくない。

Unity 対応については issue に上がっているが・・・

@amlinux @zhangzhibin I do understand the Unity support problems, since I'm working with Unity daily since many years, but I do agree with @jskeet, I believe it is not up to the world to adjust to Unity, but Unity to upgrade to a modern version of .NET, people just can't keep living in the middle-age because of Unity...

個人的にはこの意見に賛成。

手順

  1. DSL(Ruby) スキーマを用意する
  2. DSLスキーマをAvroスキーマ(.avsc) に変換
  3. .avsc から C# の IDL 生成
  4. 組み込み
  5. AOT 対応

用意するもの

1. DSL(Ruby) スキーマを用意する

任意の名称で、 ${RAILS_APP_ROOT}/avro/dsl 以下に配置します。
サーバ側の作業を増やさないことを念頭にしていますが、クライアント側で各データをどのような型で扱うべきかは最低限定義する必要があります。
実際は手で定義するなり、既存の Model や Selializer から動的生成するなどしましょう。
本稿では下記スキーマを例として利用します。

schema.rb
namespace 'example.avro.smith'

record :user do
  required :id,        :long
  required :user_name, :string
end

record :team do
  required :leader,  :user
  required :members, :array, items: :user
end

2. DSLスキーマをAvroスキーマ(.avsc) に変換

事前に Gemfile に avro-builder を追加して、アプリで利用できるようにしておきます。

gem 'avro-builder'

avro-builder が提供する rake タスクを実行します。

$ rake avro:generate

この rake タスクでは \${RAILS_APP_ROOT}/avro/dsl 以下を走査して .avsc を生成します。
.avsc は ${RAILS_APP_ROOT}/avro/schema 以下に生成されます。
中身はスキーマを JSON で表現したものです。

3. .avsc から C# の IDL 生成

Avro 本体に付属するコードジェネレータを利用します。
はじめに git からクローンしてきたソースをビルドします。

$ cd ${AVRO_DIR}
$ ./build.sh dist

その後、ビルドされたコードジェネレータを実行します。
オプションは、スキーマファイルと出力先の指定です。

$ mono ./lang/csharp/build/codegen/Release/avrogen.exe -s ./avro/schema/schema.avsc ./avro/idl

出力されたファイル名は Win 準拠になっているため、バックスラッシュで区切った出力先のパスが含まれます。
これを適宜修正します。

4. 組み込み

avrogen.exe で生成した .cs ファイルを Unity プロジェクトの Assets 以下の任意の場所に配置します。

動作を確認するためにランダムなデータを生成します。
データ生成は avro-tools で実行可能です。
avro-tools は公式で jar が提供されていますが、本稿では OSX を前提としているため brew でインストールします。

$ brew install avro-tools

ランダムデータ生成。

$ avro-tools random ./avro/random.avro --schema-file ./avro/schema/schema.avsc  --count 20 --codex null

出力した .avro を StreamingAssets 以下に配置します。
codegen から吐き出されたクラスから、正しくデータが取得できていることを確認します。

string url = Application.streamingAssetsPath + "/Avro/random.avro";

#if UNITY_IOS || UNITY_EDITOR
WWW www = new WWW("file://" + url);
#else
WWW www = new WWW(url);
#endif
while (!www.isDone) {}

using (MemoryStream stream = new MemoryStream(www.bytes)) {
    IFileReader<team> dataFileReader = DataFileReader<team>.OpenReader(stream);

    team t = null;
    while (dataFileReader.HasNext()) {
        t = dataFileReader.Next();

        Debug.Log("t.leader.id : " + t.leader.id);
        Debug.Log("t.leader.user_name : " + t.leader.user_name);

        for (int i = 0; i < t.member.Count; i++) {
            user u = t.member[i];

            Debug.Log("t.member[" + i + "].id : " + t.member[i].id);
            Debug.Log("t.member[" + i + "].user_name : " + t.member[i].user_name);
        }
    }
}

5. AOT 対応

上記で Android 及び UnityEditor では動作させることができます。
ただし Avro ランタイム中に Reflection.Emit の使用箇所があるため、 JIT の許容されていない iOS ではエラーとなり動作しません。

そのため、 Avro ランタイムのコードを修正する必要があります。
差分は下記 PR にて参照することができます。
https://github.com/apache/avro/pull/134

PR 元のブランチはこちらです。
https://github.com/dolow/avro/tree/feature/aot_support

なお、現時点ではマージされておらず、今後取り込まれる保証もないため、導入は自己責任において行ってください。

2年の時を経てマージされたのを、さらにその2年後に気付きました、やったね。


以上です。

4
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
3