Почему я не могу одновременно выполнять разные функции Matlab из Java?
У меня есть два Java-сервлета: DataFetcherServlet
а также UploaderServlet
, Оба сервлета вызывают 2 разных метода Java, которые в свою очередь вызывают соответствующие им функции Matlab через JNI, и каждый из них был скомпилирован в отдельный файл jar Java для использования в качестве библиотеки. Приложение работает на AJAX для создания ощущения рабочего стола. Для UploaderServlet
пользователи могут загрузить файл Excel в этот сервлет, затем проанализированные данные передаются в метод Java, который затем вызывает скомпилированную функцию Matlab для генерации и сохранения большого количества изображений (в настоящее время более 5000 изображений), поскольку это займет много времени Я использую ExecutorService
выполнить его в фоновом режиме. Но новые запросы отправили DataFetcherServlet
которая также будет вызывать другую скомпилированную функцию Matlab, будет заблокирована, пока не будет завершена часть создания изображения Я не знаю, почему он блокирует новые запросы, даже если запросы отправляются другому сервлету.
DataFetcherServlet.java
public class DataFetcherServlet extends HttpServlet {
@Inject
private CdfReader reader; // An EJB to get a data array from Matlab
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
try {
String filePath = "path/to/file";
Object[] result = reader.read(filePath); // reader.read() is just a wrapper around the method in the jar file mentioned above that actually calls the matlab function to return an array of number
MWNumericArray array = (MWNumericArray)result[0] // This will block while the other Matlab function is generating the images.
.
.
.
} catch (MWException ex) {
Logger.getLogger(DataFetcherServlet.class.getName()).log(Level.SEVERE, null, ex);
}
}
UploaderServlet.java
public class UploaderServlet extends HttpServlet {
@Inject
private ExcelIonImageGenerator generator; // An EJB to call Matlab to generate the images
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
try {
String dir = "path/to/parent/directory";
Path excel = Paths.get(dir+ "excel", part.getSubmittedFileName()); // Path to where the uploaded excel file is stored
if (!Files.exists(excel))
Files.copy(part.getInputStream(), excel);
// ExcelExtractor is a helper class to parse the excel file.
Double[][] ranges = ExcelExtractor.extractSheet(WorkbookFactory.create(excel.toFile()));
// This will call a Java library method which in turns call the Matlab function
// to generate the images (over 5000 in this case)
// See the code for this method below.
generator.generate(dir+ "images" + File.separator, ranges);
} catch (MWException | InvalidFormatException ex) {
Logger.getLogger(UploaderServlet.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
ExcelIonImageGenerator.java
import com.mathworks.toolbox.javabuilder.*; // Matlab SDK needed to integrate with Java
import java.util.concurrent.*;
import java.util.logging.*;
import javax.annotation.PreDestroy;
import javax.ejb.Stateless;
import save_ion_image_for_all_ranges_in_spreadsheet.Class1; // The jar file which contains code to call Matlab code through JNI
@Stateless
public class ExcelIonImageGenerator {
private final Class1 clazz1;
private ExecutorService pool;
public ExcelIonImageGenerator() throws MWException {
clazz1 = new Class1();
pool = Executors.newFixedThreadPool(1);
}
public void generate(String path, Double[][] ranges) throws MWException {
// Submit this task to the ExecutorService so it can be processed
// in a different thread than the caller thread
pool.submit(() -> generateHelper(path, ranges, clazz1), 1);
}
private void generateHelper(String path, Double[][] ranges, Class1 clazz) {
try {
// This method was generated by Matlab tool, it calls the native
// Matlab code through JNI, and it will block any request that will call
// other Matlab functions until it finishes.
clazz.save_ion_image_for_all_ranges_in_spreadsheet(path, ranges);
} catch (MWException ex) {
Logger.getLogger(ExcelIonImageGenerator.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
1 ответ
У вас есть три варианта:
- Запустите несколько процессов вашего Java-приложения, которое вызывает Matlab. Вызовы из одного процесса используют ту же MCR, которая имеет блокировку всего процесса, однако вызовы из разных процессов будут выполняться на отдельных вычислительных механизмах MCR.
- Используйте Matlab Production Server, который в основном облегчает использование нескольких MCR. Это инструментарий, для которого требуется отдельная лицензия и установка.
- Вам не обязательно ограничивать себя в запуске MCR / скомпилированного кода, если только у вас нет особых проблем с производительностью. На самом деле вы можете установить сам Matlab на сервер, запустить несколько экземпляров (без заголовка и т. Д.) Из одного и того же процесса Java и обмениваться данными с ними, например, через MatlabControl или новый официальный API-интерфейс MATLAB Engine для Java.
Существует очень хороший ответ от команды поддержки MathWorks на MatlabCentral, подробно объясняющий эти ограничения MCR.