最近尝试在 JRuby 里集成Java 工作流组件,遇到不少令人困惑的点,关于 JRuby 的资料不多,记录下一些心得。
环境和工具:Mac, rvm
rvm get head
rvm install jruby-9.0.4.0
rvm use jruby-9.0.4.0
你也可以创建一个.ruby-version
文件在项目目录,进入目录就会自动切换至 JRuby。
echo 'jruby-9.0.4.0' > .ruby-version
首先创建一个目录 getting-started-with-jruby
, 下面的代码都可以在Github找到。
创建一个 Java 文件 Hello.java
public class Hello {
public static void world(){
System.out.println("Hello JRuby!");
}
}
编译成 class 文件 javac Hello.java
在 JRuby 里require 'java'
后你可以访问所有在 classpath 里的 java classes。
创建一个 ruby 文件 calling-class-in-root.rb
require 'java'
Java::Hello.world()
在命令行运行 ruby calling-class-in-root.rb
, 看到输出 Hello JRuby!
运行 jruby 所在的目录是 classpath, 所有运行目录里的.class
文件可以在 JRuby 里访问。
classpath 可以通过设置 CLASSPATH 环境变量进行扩展
$CLASSPATH << "classes"
# or $CLASSPATH << "file:///#{File.expand_path('classes')}/"
创建一个 java 文件 java/src/main/java/SubHello.java
, 并编译。
在项目目录创建一个 ruby 文件 calling-class-in-sub-folder.rb
.
require 'java'
$CLASSPATH << "java/src/main/java"
Java::SubHello.world()
在命令行运行 ruby calling-class-in-sub-folder.rb
, 看到输出 Hello jruby in sub folder!
jar 文件须在 classpath 或者手动 require
require 'path/to/mycode.jar'
在java
目录创建pom.xml
, 运行 mvn package
打包成 demo-1.0.jar
在项目目录创建一个 ruby 文件 calling-jar.rb
,
require 'java'
require './java/target/demo-1.0.jar'
Java::SubHello.world()
在命令行运行 ruby calling-jar.rb
, 看到输出 Hello jruby in sub folder!
像bundler
一样管理 jar 依赖,首先安装
gem install jbundler
在项目目录创建Jarfile
:
jar 'commons-io:commons-io', '2.4'
在命令行运行 jbundle install
安装声明的 jar 包。
在 JRuby 里require 'jbundler'
后,你将能调用 Jarfile 里声明的包
在项目目录创建一个 ruby 文件 calling-jar-with-jbundler.rb
require 'java'
require 'jbundler'
file = java.io.File.new('./Jarfile')
lines = org.apache.commons.io.FileUtils.readLines(file, "UTF-8")
puts lines
你将看到输出 [jar 'commons-io:commons-io', '2.4']
你也可以使用驼峰风格调用 java
require 'java'
require 'jbundler'
file = Java::JavaIo::File.new('./Jarfile')
lines = Java::OrgApacheCommonsIo::FileUtils.readLines(file, "UTF-8")
puts lines
使用 "--dev" 参数等价于同时设置以下几个 JRuby 参数:
在开发过程,想要程序启动更快而不关心运行效率,--dev 参数应该不错。
RVM 提供了一个 hook 文件$rvm_path/hooks/after_use_jruby_opts
,将它变成可执行之后,每次切换至 JRuby 会根据环境变量 PROJECT_JRUBY_OPTS 添加 JRuby 的启动参数。
chmod +x $rvm_path/hooks/after_use_jruby_opts
echo 'PROJECT_JRUBY_OPTS=(--dev)' > ~/.rvmrc
[X] rails/spring 只支持 MRI Ruby, JRuby 不支持fork
[X] spork, 启动报错 TypeError: no implicit conversion of Fixnum into String
[✓] theine 与 Spork 类似
theine_server
thenine some command
[X] drip
运行rails runner
挂死
分别用 MRI Ruby 和 JRuby 创建两个 Rails 项目。
rvm use 2.2.3
gem install rails
rails new ruby-on-rails
rvm use jruby-9.0.4.0
gem install rails
rails new jruby-on-rails
time rake test
time rails s
time rails runner "puts Rails.env"
ruby | jruby | jruby --dev | theine | |
---|---|---|---|---|
rake test | 3.841s | 13.547s | 7.451s | 4.882s |
rails s | 4.796s | 20.914s | 11.833s | 5.084s |
rails runner | 2.128s | 17.116s | 9.718s | 4.464s |
总结:使用 --dev
参数可以减少大约 45% 的启动时间,使用 theine
还能进一步加速。
JRuby 的启动速度比较糟糕,运行效率又怎么样呢?
还是对两个空白的 Rails 项目进行简单的测试
Ruby on Rails | JRuby on Rails | |
---|---|---|
ab -n 1000 -c 1 | 22.103ms | 16.275ms |
ab -n 1000 -c 10 | 22.079ms | 12.622ms |
ab -n 1000 -c 50 | 22.051ms | 12.236ms |
虽然不代表真实的项目,从结果来看 JRuby 的运行效率是不错的。