3

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

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

问题

  1. 这样的设置甚至可能吗?
  2. 我需要做什么来完成它?我什至不确定这本身是一个 JRuby 问题,还是需要在 Web 服务器中配置的东西。我有使用Passenger 和Unicorn/nginx 的经验,但没有使用Java 服务器的经验,所以如果这确实涉及配置Java 服务器(如Tomcat),任何有关这方面的信息都会有所帮助。

我真的不知道从哪里开始寻找,或者是否有更好的方法来解决这个问题,所以任何和所有建议或相关链接都表示赞赏。

4

2 回答 2

2

以下是有关如何将 sinatra 应用程序部署到Tomcat的一些说明。

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

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

于 2013-04-27T14:14:28.323 回答
2

是的,它是可能的设置(见下文关于部署),为了完成它,我建议使用单例

Jruby 中的单例

参考问题:在机架安装的应用程序/中间件堆栈之间共享对象的最佳/最优雅的方式?我同意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

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

#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)进行部署。来源:jruby 部署

使用构建在 JBoss 应用服务器上的 TorqueBox。JBoss AS 包括高性能集群、缓存和消息传递功能。

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

线程同步

Sinatra 将使用 Mutex#synchronize 方法在每个请求上放置一个锁,以避免线程之间的竞争条件。如果您的 sinatra 应用程序是多线程的并且不是线程安全的,或者您使用的任何 gem 都不是线程安全的,您会想要执行 set :locktrue以便在给定时间只处理一个请求。.. 否则默认lockfalse,这意味着synchronize将直接屈服于块。

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

于 2013-04-29T11:36:05.433 回答