JRuby:从机架应用程序调用Java代码并将其保留在内存中

时间:2013-04-24 20:00:12

标签: java sinatra jruby rack

我目前了解Java和Ruby,但从未使用过JRuby。我想在Rack(sinatra)Web应用程序中使用一些RAM和计算密集型Java代码。特别是,这个Java代码将大约200MB的数据加载到RAM中,并提供了使用这种内存数据进行各种计算的方法。

我知道可以在JRuby中从Ruby调用Java代码,但在我的情况下还有一个额外的要求:这个Java代码需要加载一次,保存在内存中,并作为共享资源保存。 sinatra代码(由多个Web请求触发)调用。

问题

  1. 这样的设置是否可能?
  2. 完成它需要做什么?我甚至不确定这是一个JRuby问题本身,还是需要在Web服务器中配置的东西。我有使用Passenger和Unicorn / nginx的经验,但没有Java服务器的经验,所以如果这涉及配置Tomcat等Java服务器,那么任何关于它的信息都会有所帮助。
  3. 我真的不确定从哪里开始寻找,或者是否有更好的方法来解决这个问题,所以任何和所有建议或相关链接都会受到赞赏。

2 个答案:

答案 0 :(得分:2)

Here是有关如何将sinatra应用部署到Tomcat的一些说明。

如果您保留对已加载的java实例的引用,则可以加载一次java代码并重复使用。您可以在ruby中保留全局变量的引用。

需要注意的一点是,您使用的java库可能不是线程安全的。如果您在tomact中运行ruby代码,则可以同时执行多个请求,并且这些请求可能都会访问您的共享Java库。如果您的库不是线程安全的,则必须使用某种synchronization来阻止多个线程访问它。

答案 1 :(得分:2)

是的,可以设置(见下文有关部署)并完成它我建议使用Singleton

Jruby中的单身人士

参考问题:best/most elegant way to share objects between a stack of rack mounted apps/middlewares?我同意Colin Surprenant的回答,即单例作为模块模式,我更喜欢使用单例mixin

实施例

我在这里发布了一些可用作概念证明的测试代码:

JRuby sinatra方面:

#file: sample_app.rb

require 'sinatra/base' 
require 'java' #https://github.com/jruby/jruby/wiki/CallingJavaFromJRuby
java_import org.rondadev.samples.StatefulCalculator #import you java class here

# singleton-as-module loaded once, kept in memory
module App
  module Global extend self    
    def calc
      @calc ||= StatefulCalculator.new 
    end
  end
end
# you could call a method to load data in the statefull java object
App::Global.calc.turn_on  

class Sample < Sinatra::Base
  get '/' do
     "Welcome, calculator register:#{App::Global.calc.display}"
  end

  get '/add_one' do
    "added one to calculator register, new value:#{App::Global.calc.add(1)}"
  end
end

您可以使用trinidad或仅使用rackup config.ru在tomcat中启动它,但您需要:

#file: config.ru
root = File.dirname(__FILE__)            # => "."
require File.join( root, 'sample_app' )  # => true
run Sample  # ..in sample_app.rb ..class Sample < Sinatra::Base

关于Java方面的事情:

package org.rondadev.samples;

public class StatefulCalculator {

        private StatelessCalculator calculator;

        double register = 0;

        public double add(double a) {       
            register = calculator.add(register, a); 
            return register;
        }

        public double display() {       
            return register;
        }

        public void clean() {
            register = 0;       
        }

        public void turnOff() {
            calculator = null;
            System.out.println("[StatefulCalculator] Good bye ! ");
        }

        public void turnOn() {
            calculator = new StatelessCalculator();
            System.out.println("[StatefulCalculator] Welcome !");
        }   
}

请注意,此处的register只是double,但在您的真实代码中,您可以在真实场景中拥有大数据结构

部署

您可以使用Mongrel,Thin(实验性),Webrick(但是谁会这样做?),甚至以Java为中心的应用程序容器(如Glassfish,Tomcat或JBoss)进行部署。 source: jruby deployments

使用基于JBoss Application Server构建的TorqueBox。 JBoss AS包括高性能集群,缓存和消息传递功能。

trinidad是一个RubyGem,允许您在嵌入式Apache Tomcat容器中运行任何基于Rack的applet包装

线程同步

Sinatra将使用Mutex#synchronize方法锁定每个请求,以避免线程之间的竞争条件。如果您的sinatra应用程序是多线程的并且不是线程安全的,或者您使用的任何gem都不是线程安全的,那么您可能希望设置:locktrue以便在给定时间只处理一个请求。 ..否则默认情况下lockfalse,这意味着synchronize将直接收益。

来源:https://github.com/zhengjia/sinatra-explained/blob/master/app/tutorial_2/tutorial_2.md