LoginSignup
4
0

More than 1 year has passed since last update.

Rustでmrubyを呼んで見るライブラリminutus

Posted at

目的

Rustでプログラム実行中に後から渡されるプログラムを実行したいです。プラグインみたいな感じです。
Rustだけで実現するのは難しいので(wasmあたりを使うとできるという噂もありますが)、他のプログラム言語を利用します。
ここではRubyを使います。

Rubyを使うものは当然Rubyがインストールされている必要があり、シングルバイナリで実行できないのが難点です。
そこでmrubyを使うことになるんですが、以前からあったものは古いmrubyしか対応していなくて微妙でした。
今回利用するminutusというライブラリは最新のmrubyをサポートしていて、mrubyの知識があまりなくてもサクッと利用できるのがよかったです。
開発の意図などは作者のブログを参考にしてください。Minutus という mruby の Rust バインディングを作った

コードの目的

Rubyのコードでは以下の2点を確認しました。

  • 引数を渡せる
  • HTTP通信ができる

コード

以下を使うことで任意のmrubyのモジュールが組み込めます。今回は通信が確認できればいいんですが、色々入ってますが、気にしないでください。

build_config.rb
MRuby::Build.new do |conf|
  toolchain
  conf.gembox 'default'
  conf.gem github: 'mattn/mruby-json'
  conf.gem :git => 'https://github.com/iij/mruby-pack.git'
  conf.gem :git => 'https://github.com/iij/mruby-digest.git'
  conf.gem :git => 'https://github.com/mattn/mruby-json.git'
  conf.gem :git => 'https://github.com/mattn/mruby-uv.git'
  conf.gem :git => 'https://github.com/mattn/mruby-http.git'
  conf.gem :git => 'https://github.com/matsumoto-r/mruby-simplehttp.git'
  conf.gem :git => 'https://github.com/matsumoto-r/mruby-httprequest.git'
  conf.gem :git => 'https://github.com/matsumoto-r/mruby-oauth.git'
  conf.gem :git => 'https://github.com/matsumoto-r/mruby-sleep.git'
  conf.gem :git => 'https://github.com/matsumoto-r/mruby-zabbix.git'
  conf.gem :git => 'https://github.com/matsumoto-r/mruby-growthforecast.git'
  conf.gem :git => 'https://github.com/y-ken/fluent-logger-mruby'
end

Rubyのコードは以下のようになります。
メソッド形式になっていてRustから引数を渡すことになります。
コメントアウトの除くときちんと標準出力に結果が表示されました。

some_script.rb
def execute(url)
  h = HttpRequest.new
  b = h.get(url).body
  # p b
  j = JSON.parse(b)
  j.to_json
end
Cargo.toml
[package]
name = "mruby"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
minutus = { version = "0.4.0", features = ["mruby_3_1_0", "link_mruby"] }
serde_json = "1"

[build-dependencies]
minutus = { version = "0.4.0", features = ["mruby_3_1_0", "link_mruby"] }
build.rs
fn main() {
    println!("cargo:rerun-if-changed=bulid.rs");
    println!("cargo:rerun-if-changed=build_config.rb");
    minutus::MRubyManager::new()
        .build_config(&std::env::current_dir().unwrap().join("build_config.rb"))
        .mruby_version("3.1.0")
        .run();
}
main.rs
use std::time::SystemTime;

minutus::define_funcall!{
    fn execute(self, url: String) -> String;
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let evaluator = minutus::Evaluator::build();
    let script = std::fs::read_to_string("some_script.rb")?;
    evaluator.evaluate(&script)?;
    let main = evaluator.evaluate("self")?;
    for i in 0..5 {
        let start = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_millis();
        let retval = main.execute(format!("https://httpbin.org/get?i={}", i))?;
        let end = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_millis();
        let value = serde_json::from_str::<serde_json::Value>(&retval)?;
        println!("{}", serde_json::to_string(&value)?);
        println!("{}", end - start);
    }
    
    Ok(())
}

結果

結果
{"args":{"i":"0"},"headers":{"Accept":"*/*","Host":"httpbin.org","X-Amzn-Trace-Id":"Root=1-634c8dac-79737ca70d71764e2129a0f8"},"origin":"150.91.1.193","url":"https://httpbin.org/get?i=0"}
6140
{"args":{"i":"1"},"headers":{"Accept":"*/*","Host":"httpbin.org","X-Amzn-Trace-Id":"Root=1-634c8dad-2b6d385521217ec222b742df"},"origin":"150.91.1.193","url":"https://httpbin.org/get?i=1"}
976
{"args":{"i":"2"},"headers":{"Accept":"*/*","Host":"httpbin.org","X-Amzn-Trace-Id":"Root=1-634c8dae-764a40ac0159daff07e84dc7"},"origin":"150.91.1.193","url":"https://httpbin.org/get?i=2"}
968
{"args":{"i":"3"},"headers":{"Accept":"*/*","Host":"httpbin.org","X-Amzn-Trace-Id":"Root=1-634c8daf-6689f5be0cbb4348632cc014"},"origin":"150.91.1.193","url":"https://httpbin.org/get?i=3"}
969
{"args":{"i":"4"},"headers":{"Accept":"*/*","Host":"httpbin.org","X-Amzn-Trace-Id":"Root=1-634c8db0-43cf22690ec6f43710a87ef5"},"origin":"150.91.1.193","url":"https://httpbin.org/get?i=4"}
1051

どうももっさりしています。何が原因かみてみます。

JSONを疑る

本来ならJSONをRubyのオブジェクトにして加工したいこともあるんですが、今は外してみました。

def execute(url)
  h = HttpRequest.new
  h.get(url).body
end
5980
1004
1001
951
956

変わらないです。

通信を疑る

def execute(url)
  # h = HttpRequest.new
  # h.get(url).body
  # p b
  # j = JSON.parse(b)
  # j.to_json
  { url: url }.to_json
end

0
0
0
0
0

あ、これですね。

通信先を疑る

time curl "https://httpbin.org/get?i=0"
{
  "args": {
    "i": "0"
  }, 
  "headers": {
    "Accept": "*/*", 
    "Host": "httpbin.org", 
    "User-Agent": "curl/7.68.0", 
    "X-Amzn-Trace-Id": "Root=1-634c964f-36852b3112cfa5923e8ac2f2"
  }, 
  "origin": "150.91.1.193", 
  "url": "https://httpbin.org/get?i=0"
}

real    0m5.759s
user    0m0.013s
sys     0m0.007s

犯人はこれでした。早いときもあれば遅いときもあります。プログラムで最初だけ思いのかと思ったらそうでは無くて、接続先の問題のようでした。

まとめ

mruby自体のパフォーマンスは問題なさそうです。

組み込みも簡単ですが、コンパイル時に利用するライブラリが決まっていないといけないので、後からmrubyでライブラリが必要になった時は再ビルドする必要があります。

4
0
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
0