2017-02-14 104 views
0

Matlab-compiled-to-Java codeMatlab的Java的互操作性

我们的web应用程序作为一个集成层,它允许用户运行Matlab代码(MATLAB是一种科学的编程语言),将其编译成Java,通过浏览器打包为jar文件(选择如上图所示,除了remote_proxy-1.0.0.jar这不是,它用于RMI)。

问题是,包含在javabuilder-1.0.0.jar文件中的Matlab Java运行时,有一个进程范围的阻塞机制,这意味着如果第一个用户发送HTTP请求来执行cdf_read-1.0.0.jar或任何Matlab编译成Java罐那么随后的请求将会阻塞,直到第一个请求完成为止,并且自从JNI用于调用本地Matlab代码并且由于应用服务器只是产生了新的线程来为每个请求提供服务,它将会持续不少于5秒,但是再一次,由于Matlab Java运行时的进程范围锁定机制,这些新产生的线程将阻止等待第一个请求被执行,因此我们的应用程序可以在技术上一次为一个用户提供服务。

所以为了解决这个问题,对于每一个这样的请求,我们启动一个新的JVM进程,将请求发送到这个新进程以使用RMI运行作业,然后将结果返回给应用服务器进程,然后销毁产生的过程。所以我们已经解决了阻塞问题,但就内存使用而言这并不是很好,这是一个利基应用,所以用户数量在thoudsands范围内。以下是用于启动新进程的代码,用于运行启动新RMI注册表的类BootStrap,并绑定远程对象以运行作业。

package rmi; 

import java.io.*; 
import java.nio.file.*; 
import static java.util.stream.Collectors.joining; 
import java.util.stream.Stream; 
import javax.enterprise.concurrent.ManagedExecutorService; 
import org.slf4j.LoggerFactory; 
//TODO: Remove sout 
public class ProcessInit { 

    public static Process startRMIServer(ManagedExecutorService pool, String WEBINF, int port, String jar) { 
     ProcessBuilder pb = new ProcessBuilder(); 
     Path wd = Paths.get(WEBINF); 
     pb.directory(wd.resolve("classes").toFile()); 
     Path lib = wd.resolve("lib"); 
     String cp = Stream.of("javabuilder-1.0.0.jar", "remote_proxy-1.0.0.jar", jar) 
       .map(e -> lib.resolve(e).toString()) 
       .collect(joining(File.pathSeparator)); 
     pb.command("java", "-cp", "." + File.pathSeparator + cp, "rmi.BootStrap", String.valueOf(port)); 
     while (true) { 
      try { 
       Process p = pb.start(); 
       pool.execute(() -> flushIStream(p.getInputStream())); 
       pool.execute(() -> flushIStream(p.getErrorStream())); 
       return p; 
      } catch (Exception ex) { 
       ex.printStackTrace(); 
       System.out.println("Retrying...."); 
      } 
     } 
    } 

    private static void flushIStream(InputStream is) { 
     try (BufferedReader br = new BufferedReader(new InputStreamReader(is))) { 
      br.lines().forEach(System.out::println); 
     } catch (IOException ex) { 
      LoggerFactory.getLogger(ProcessInit.class.getName()).error(ex.getMessage()); 
     } 
    } 
} 

这个类是用来启动一个新的RMI注册表,使每个HTTP请求来执行Matlab代码可以在一个单独的进程中运行,我们这样做是因为每个RMI注册绑定到工艺的原因,所以我们需要为每个JVM进程分别注册。

package rmi; 

import java.rmi.RemoteException; 
import java.rmi.registry.*; 
import java.rmi.server.UnicastRemoteObject; 
import java.util.logging.*; 
import remote_proxy.*; 
//TODO: Remove sout 
public class BootStrap { 

    public static void main(String[] args) { 
     int port = Integer.parseInt(args[0]); 
     System.out.println("Instantiating a task runner implementation on port: " + port); 
     try { 
      System.setProperty("java.rmi.server.hostname", "localhost"); 
      TaskRunner runner = new TaskRunnerRemoteObject(); 
      TaskRunner stub = (TaskRunner)UnicastRemoteObject.exportObject(runner, 0); 
      Registry reg = LocateRegistry.createRegistry(port); 
      reg.rebind("runner" + port, stub); 
     } catch (RemoteException ex) { 
      Logger.getLogger(BootStrap.class.getName()).log(Level.SEVERE, null, ex); 
     } 
    } 
} 

该类允许提交执行Matlab代码的请求,返回结果并杀死新产生的进程。

package rmi.tasks; 

import java.rmi.*; 
import java.rmi.registry.*; 
import java.util.Random; 
import java.util.concurrent.*; 
import java.util.logging.*; 
import javax.enterprise.concurrent.ManagedExecutorService; 
import remote_proxy.*; 
import rmi.ProcessInit; 

public final class Tasks { 

    /** 
    * @param pool This instance should be injected using @Resource(name = "java:comp/DefaultManagedExecutorService") 
    * @param task This is an implementation of the Task interface, this 
    *    implementation should extend from MATLAB class and accept any necessary 
    *    arguments, e.g Class1 and it must implement Serializable interface 
    * @param WEBINF WEB-INF directory 
    * @param jar Name of the jar that contains this MATLAB function 
    * @throws RemoteException 
    * @throws NotBoundException 
    */ 
    public static final <T> T runTask(ManagedExecutorService pool, Task<T> task, String WEBINF, String jar) throws RemoteException, NotBoundException { 
     int port = new Random().nextInt(1000) + 2000; 
     Future<Process> process = pool.submit(() -> ProcessInit.startRMIServer(pool, WEBINF, port, jar)); 
     Registry reg = LocateRegistry.getRegistry(port); 
     TaskRunner generator = (TaskRunner) reg.lookup("runner" + port); 
     T result = generator.runTask(task); 
     destroyProcess(process); 
     return result; 
    } 

    private static void destroyProcess(Future<Process> process) { 
     try { 
      System.out.println("KILLING THIS PROCESS"); 
      process.get().destroy(); 
      System.out.println("DONE KILLING THIS PROCESS"); 
     } catch (InterruptedException | ExecutionException ex) { 
      Logger.getLogger(Tasks.class.getName()).log(Level.SEVERE, null, ex); 
      System.out.println("DONE KILLING THIS PROCESS"); 
     } 
    } 
} 

的问题:

  • 我一定要开始一个新的独立的RMI注册表,并远程将其绑定为每个新工艺?
  • 有没有更好的方法来达到同样的效果?

回答

1
  1. 您不希望JVM启动时间成为感知交易时间的一部分。我会提前启动大量的RMI JVM,这取决于期望的并发请求数,可能会有数百甚至上千个。
  2. 您只需要一个注册表:rmiregistry.exe。从它的默认端口启动并使用适当的CLASSPATH,以便它可以找到它们依赖的所有存根和应用程序类。
  3. 将每个远程对象绑定到该注册表中,并按顺序增加一般形式runner%d的名称。
  4. 让您的RMI客户端从已知范围1-N随机选择一个'跑步者',其中N是跑步者人数。您可能需要比单纯的随机性更复杂的机制来确保跑步者在当时是空闲的。

您不需要多个注册表端口或甚至多个注册表。

+0

对于#1,当我开始实施我的当前解决方案时,我也考虑过这个问题,但他们占用了太多RAM,速度并不是优先考虑的事情,他们只是想通过网络使用Matlab。 #2,我不知道这样的可执行文件存在。谢谢,我会尽力让你知道我有空时是如何摆脱的。现在我正在做一些代码重构,所以可能需要一些时间 – Dummy