2025/06/18 21:08 Calling Go from Elixir with a CNode in Crystal

やあ、ロボ子!今日はMozi社のElixirアプリとGo製バックエンドの連携についての記事を見つけたのじゃ。

博士、こんにちは。ElixirとGoの連携ですか、面白そうですね。どのような内容なのでしょう?

Mozi社は、Elixir Phoenix LiveViewアプリから既存のGo製バックエンドを呼び出したかったらしいのじゃ。イベント処理コードの重複を避けるためにね。

なるほど。それで、どのような解決策を検討したのでしょうか?

最初はNIFs(Native Implemented Functions)を考えたみたいじゃ。GoコードをC ABIライブラリにコンパイルして、Elixirから呼び出す方法じゃ。

NIFsですね。でも、記事では不採用とされていますね。何か問題があったのでしょうか?

そうじゃ。2つのランタイムがリソースを奪い合ったり、CコードのバグでElixirアプリがクラッシュする可能性があるからじゃ。それに、ビルドプロセスも複雑になるみたいじゃし。

なるほど、リスクが高いのですね。他に検討された解決策はありますか?

Portsも検討したみたいじゃな。BEAMプロセスの制御下で別のプロセスとして実行する方法じゃけど、NIFsよりはマシだけど、オーバーヘッドが高いし、完全な分離はできないみたいじゃ。

それで、最終的にどのような解決策を採用したのでしょうか?

C Nodeを採用したのじゃ!Erlangの`erl_interface`ライブラリを使って、BEAMディストリビューションノードをCで実装する方法じゃ。

C Nodeですか。具体的にどのような利点があるのでしょうか?

コンパイル時と実行時の両方でコードベースを完全に分離できるのが大きいみたいじゃな。Elixir側で軽量なラッパーライブラリを作って、リモートノードをElixir関数のように呼び出せるのじゃ。

なるほど、分離性が高いのですね。実装はどのように行ったのでしょうか?

GoコードをC ABIライブラリとしてビルドして、CラッパーでCLI引数とか環境変数を処理して、受信メッセージを処理するループを開始するのじゃ。CコードからGoコードを呼び出すみたいじゃな。

CコードがElixirアプリに接続するのですね。Elixir側ではどのように接続状態を確認するのでしょうか?

`Node.list(:hidden)`でCノードの接続状態を確認できるみたいじゃ。

その後、CコードをCrystalに書き換えたとありますね。なぜCrystalを選んだのでしょうか?

Cコードの保守性を向上させるためじゃな。CrystalはRubyに似た構文でネイティブコードにコンパイルされるから、Cより扱いやすいのじゃ。

Goのロギング設定などをCrystalコードから利用できるように、Goから追加の関数を公開したともありますね。

そうじゃ。細かいけど、こういう連携が大事なのじゃ。

デプロイメントはどのように行ったのでしょうか?

Crystal/Goコードを単一のDockerコンテナとしてビルドして、Elixirアプリと同じKubernetesポッドで実行するみたいじゃ。macOSでローカル開発して、Linuxでビルドとデプロイができるのも便利じゃな。

課題もあったようですね。Alpine Linux上でGo 1.24コンパイラのカスタムビルドが必要だったと。

MUSL libcのサポートのためじゃな。こういう細かいところがハマりどころなのじゃ。

今後、`erl_interface`のCrystalラッパーをオープンソース化する可能性があるとのことですね。

それは楽しみじゃな!みんなが使えるようになると、もっとElixirとGoの連携が進むかもしれないのじゃ。

今回の記事は、ElixirとGoの連携における様々な解決策と、それぞれのトレードオフについて学ぶことができました。大変勉強になりました。

そうじゃな!しかし、ロボ子よ、これだけ賢いロボットなのに、どうして私に色々聞くのじゃ?まさか、私をからかっているのか?

まさか!博士の知識は深淵で、いつも驚かされることばかりです。それに、博士の解説はとても面白いので、ついつい質問してしまうのです。

むむ、そうか。まあ、褒め言葉として受け取っておくかのじゃ!
⚠️この記事は生成AIによるコンテンツを含み、ハルシネーションの可能性があります。