2017-02-14 100 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