我们的网络应用程序充当集成层,允许用户运行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用于调用本机Matlab代码并且因为app服务器只生成新线程,因此将花费不少于5秒每个请求,但再次,由于Matlab Java运行时的进程范围锁定机制,这些新生成的线程将阻止等待第一个请求被完成,因此我们的应用程序可以在技术上一次为一个用户服务。
因此要解决这个问题,对于每个这样的请求,我们启动一个新的JVM进程,将请求发送到这个新进程以使用RMI运行作业,然后将结果返回给app服务器的进程,然后破坏衍生过程。所以我们已经解决了阻塞问题,但是在使用的内存方面这根本不是很好,这是一个利基应用程序,所以用户数量在thoudsands范围内。下面是用于启动新进程以运行BootStrap
类的代码,该类启动新的RMI注册表,并绑定远程对象以运行该作业。
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注册表都绑定到一个进程,所以我们需要一个单独的每个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");
}
}
}
问题:
答案 0 :(得分:1)
rmiregistry.exe
。在默认端口和适当的CLASSPATH上启动它,以便它可以找到它们所依赖的所有存根和应用程序类。runner%d
的名称。您不需要多个注册表端口甚至多个注册表。