简体   繁体   English

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

[英]JRuby: Calling Java Code From A Rack App And Keeping It In Memory

I currently know Java and Ruby, but have never used JRuby. 我目前了解Java和Ruby,但从未使用过JRuby。 I want to use some RAM- and computation-intensive Java code inside a Rack (sinatra) web application. 我想在Rack(sinatra)Web应用程序中使用一些RAM和计算密集型Java代码。 In particular, this Java code loads about 200MB of data into RAM, and provides methods for doing various calculations that use this in-memory data. 特别是,这个Java代码将大约200MB的数据加载到RAM中,并提供了使用这种内存数据进行各种计算的方法。

I know it is possible to call Java code from Ruby in JRuby, but in my case there is an additional requirement: This Java code would need to be loaded once, kept in memory, and kept available as a shared resource for the sinatra code (which is being triggered by multiple web requests) to call out to. 我知道可以在JRuby中从Ruby调用Java代码,但在我的情况下还有一个额外的要求:这个Java代码需要加载一次,保存在内存中,并作为sinatra代码的共享资源保持可用(这是由多个Web请求触发的呼叫。

Questions 问题

  1. Is a setup like this even possible? 这样的设置是否可能?
  2. What would I need to do to accomplish it? 我需要做些什么才能完成它? I am not even sure if this is a JRuby question per se, or something that would need to be configured in the web server. 我甚至不确定这是一个JRuby问题本身,还是需要在Web服务器中配置的东西。 I have experience with Passenger and Unicorn/nginx, but not with Java servers, so if this does involve configuration of a Java server such as Tomcat, any info about that would help. 我有使用Passenger和Unicorn / nginx的经验,但没有Java服务器的经验,所以如果这涉及配置Tomcat等Java服务器,任何有关这方面的信息都会有所帮助。

I am really not sure where to even start looking, or if there is a better way to be approaching this problem, so any and all recommendations or relevant links are appreciated. 我真的不确定从哪里开始寻找,或者是否有更好的方法来解决这个问题,所以任何和所有的建议或相关链接都表示赞赏。

Here are some instructions for how to deploy a sinatra app to Tomcat . 以下是有关如何将sinatra应用程序部署到Tomcat的一些说明。

The java code can be loaded once and reused if you keep a reference to the java instances you have loaded. 如果您保留对已加载的java实例的引用,则可以加载一次java代码并重用它们。 You can keep a reference from a global variable in ruby. 您可以在ruby中保留全局变量的引用。

One thing to be aware of is that the java library you are using may not be thread safe. 需要注意的一点是,您使用的Java库可能不是线程安全的。 If you are running your ruby code in tomact, multiple requests can execute concurrently, and those requests may all access your shared java library. 如果您在tomact中运行ruby代码,则可以同时执行多个请求,并且这些请求可能都会访问您的共享Java库。 If your library is not thread safe, you will have to use some sort of synchronization to prevent multiple threads accessing it. 如果您的库不是线程安全的,则必须使用某种同步来防止多个线程访问它。

Yes, a setup it's possibile ( see below about Deployment ) and to accomplish it I would suggest to use a Singleton 是的,一个可能的设置(见下面有关部署)并完成它我建议使用Singleton

Singletons in Jruby Jruby中的单身人士

with reference to question: best/most elegant way to share objects between a stack of rack mounted apps/middlewares? 参考问题: 在一堆机架式应用程序/中间件之间共享对象的最佳/最优雅方式? I agree with Colin Surprenant 's answer, namely singleton-as-module pattern which I prefer over using the singleton mixin 我同意Colin Surprenant的回答,即单例作为模块模式,我更喜欢使用单例mixin

Example

I post here some test code you can use as a proof of concept: 我在这里发布一些测试代码,您可以将其用作概念证明:

JRuby sinatra side: 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

You can start it in tomcat with trinidad or simply with rackup config.ru but you need: 您可以使用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

something about the Java Side: 关于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 !");
        }   
}

Please note that the register in here is only a double but in your real code you can have a big data structure in your real scenario 请注意,这里的register只有double但在实际代码中,您可以在真实场景中拥有大数据结构

Deployment 部署

You can deploy using Mongrel, Thin (experimental), Webrick (but who would do that?), and even Java-centric application containers like Glassfish, Tomcat, or JBoss. 您可以使用Mongrel,Thin(实验性),Webrick(但是谁会这样做?),甚至以Java为中心的应用程序容器(如Glassfish,Tomcat或JBoss)进行部署。 source: jruby deployments 来源:jruby部署

with TorqueBox that is built on the JBoss Application Server. 使用基于JBoss Application Server构建的TorqueBox。 JBoss AS includes high-performance clustering, caching and messaging functionality. JBoss AS包括高性能集群,缓存和消息传递功能。

trinidad is a RubyGem that allows you to run any Rack based applet wrap within an embedded Apache Tomcat container trinidad是一个RubyGem,允许您在嵌入式Apache Tomcat容器中运行任何基于Rack的applet包装

Thread synchronization 线程同步

Sinatra will use Mutex#synchronize method to place a lock on every request to avoid race conditions among threads. Sinatra将使用Mutex#synchronize方法锁定每个请求,以避免线程之间的竞争条件。 If your sinatra app is multithreaded and not thread safe, or any gems you use is not thread safe, you would want to do set :lock , true so that only one request is processed at a given time. 如果您的sinatra应用程序是多线程的并且不是线程安全的,或者您使用的任何gem都不是线程安全的,那么您可能希望执行set :locktrue以便在给定时间只处理一个请求。 .. Otherwise by default lock is false , which means the synchronize would yield to the block directly. ..否则默认情况下lockfalse ,这意味着synchronize将直接产生块。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM