gpt4 book ai didi

java - Matlab Java 互操作性

转载 作者:行者123 更新时间:2023-12-01 08:53:13 26 4
gpt4 key购买 nike

Matlab-compiled-to-Java code

我们的 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 的 jar 文件,则后续请求将阻塞,直到第一个请求完成,并且由于 JNI 已完成,因此将花费不少于 5 秒的时间用于调用 native Matlab 代码,并且因为应用程序服务器只是生成新线程来服务每个请求,但同样,由于 Matlab Java 运行时的进程范围锁定机制,这些新生成的线程将仅阻塞等待第一个请求来实现,因此我们的应用程序在技术上可以一次为一个用户提供服务。

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

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注册表,这样每个执行Matlab代码的HTTP请求都可以在一个单独的进程中运行,我们这样做的原因是因为每个RMI注册表都绑定(bind)到一个进程,所以我们需要一个单独的进程每个 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 注册表并为每个新进程绑定(bind)一个远程 Controller ?
  • 是否有更好的方法来达到相同的结果?

最佳答案

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

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

关于java - Matlab Java 互操作性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42233498/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com