Иногда вам нужно вызвать какой-нибудь R-скрипт из Quarkus. Один из вариантов — создать микросервис, но что, если это что-то очень маленькое, и вы просто хотите вызывать его в одном месте, не перегружая архитектуру проекта. Здесь на помощь приходит GraalVM. С GraalVm вы можете выполнять R-скрипты прямо из Java без каких-либо преобразований, микросервисов или конечных точек. Мы уже видели пример на Python, посмотрим, как это сделать на R.

Для более удобного копирования и вставки, пожалуйста, откройте эту статью на сайте quarkify.

Настройка среды

Будем считать, что вы уже установили GraalVM, если нет, то перейдите по этой ссылке. Нам нужно установить интерпретатор GraalVM R:

gu install r

Помимо этой команды, нам нужно установить некоторые локальные зависимости. Для Ubuntu 18.04 это:

sudo apt-get install install libgfortran-8-dev libgomp1 build-essential gfortran-8 libxml2 libc++-dev

Для Ubuntu 19.10 это:

sudo apt-get install libgfortran-8-dev libgomp1 build-essential gfortran libxml2 libc++-dev

Запуск кода R в Quarkus

Теперь вы готовы использовать язык R. Поскольку наш пример Коэна D на Python можно выполнить одной строкой на R, мы воспользуемся в нашем случае чем-то более интересным, давайте построим логистическую регрессию, которая определит, когда нам нужно включить охлаждение процессора. В нашем случае у нас есть следующий простой набор данных

Если вы посмотрите на набор данных, вы увидите, что мы «вручную» включили охлаждение, когда процессор был выше 60 градусов. Давайте создадим сервис, который будет предсказывать, когда нам нужно его включить. Мы начнем со следующего кода R:

cpu_grads <- c(1.0,20.0, 30.0, 40.0,35.0, 37.5, 60.0, 68, 100, 77, 85, 99)
# We enable cooler around above 60 grad manually 
cpu_cooler_enable <- c(0,0,0,0,0,0,1,1,1,1,1,1)
y<-cpu_cooler_enable; x<-cpu_grads;
cpu_model <-glm(y~x, family = 'binomial')
new_data <- data.frame(x=c(40)) # c(40) is our input
predict(cpu_model, new_data, type="response")

Этот сверхпростой фрагмент кода R будет обучать логистическую регрессию и после этого предсказывать, следует ли охлаждать ЦП до 40 градусов.

Создать проект Quarkus

Если лень писать код, клонируйте наш проект с гитхаба. Создадим простое приложение Quarkus и соединим его с нашим кодом на R.

mvn io.quarkus:quarkus-maven-plugin:1.3.2.Final:create \
 -DprojectGroupId=tech.donau.quarkify \
 -DprojectArtifactId=quarkus-r-cpu-cooling \ 
 -DclassName="tech.donau.quarkify.CpuCoolingResource" \ 
 -Dpath="/cooling"

Как только он будет создан, поместите наш код R в файл по адресу src/main/resources/cpu_cooling.R, который был указан выше.

Теперь давайте изменим наш ресурс, откроем src/main/java/tech/donau/quarkify/CpuCoolingResource.java и поместим следующий код в суть.

Что мы только что сделали, так это создали полиглот Context и выполнили наш cpu_cooling.R. Поскольку наш код R выводит предсказание в качестве последнего вывода, мы можем использовать return Value. Value — это универсальная обертка для любого вывода, с помощью которой вы можете указать, какой тип вы хотите получить, так как наш R-код выводит двойной, мы назвали Value::asDouble. Если значение больше 0,8 (или 80%), мы говорим, что мы. Только представьте, сколько строк кода вы сэкономили, чтобы сохранить логистическую регрессию в коде R. Начнем наш сервис.

Запустите и протестируйте

./mvnw quarkus:dev

Теперь давайте вызовем нашу конечную точку с помощью curl (или браузера).

curl http://localhost:8080/cooling 
# No need to enable cooling

Как видите, в нашем R-коде мы указали, что хотим предсказать, хотим ли мы включить охлаждение на 40 градусов. Поскольку наша сеть обучалась на наборе данных, который возвращает истинное значение, близкое к 60 градусам, 40 выходит за пределы допустимого диапазона.

Выполнение функций R из Java

В предыдущем примере мы использовали только простой скрипт без ввода и вывода. Давайте немного изменим наш скрипт cpu_cooling.R, чтобы у нас была функция, которую мы можем выполнить при необходимости. Если вы используете клонированное репо, выполните git checkout feature/input

cpu_grads <- c(1.0,20.0, 30.0, 40.0,35.0, 37.5, 60.0, 68, 100, 77, 85, 99)
# We enable cooler around above 60 grad manually
cpu_cooler_enable <- c(0,0,0,0,0,0,1,1,1,1,1,1)
y<-cpu_cooler_enable; x<-cpu_grads;
cpu_model <- glm(y~x, family = 'binomial')
needs_cooling <- function (value) {
  new_data <- data.frame(x=c(value))
  return(predict(cpu_model, new_data, type="response"))
}

Теперь мы можем использовать функцию needs_cooling из Java. Давайте немного перепишем CpuCoolingResource.java.

В новой версии мы переместили выполнение нашего кода R в прослушиватель событий запуска onStart. Это исправит наше долгое ожидание выполнения первого вызова. Мы также получили функцию R непосредственно из контекста и выполнили ее. Вы можете видеть, насколько это просто, даже если мы сравним с Java Reflection API, он выглядит намного лучше. Давайте поиграем с нашим новым кодом. Если вы остановили сервер quarkus, повторно запустите его с помощью ./mvnw quarkus:dev.

curl http://localhost:8080/cooling?grads=33
# No need to enable cooling
curl http://localhost:8080/cooling?grads=64
# Enable cooling

Как видите, наша обученная модель в R выдает правильные результаты, а по предоставленным данным начинает говорить, что нам нужно включить охлаждение начиная с 51 град. Вы также можете увидеть, насколько это быстро.

В заключении

Если для Python у вас ограниченное количество поддерживаемых библиотек, R можно использовать практически без ограничений. Это обеспечивает интеграцию Quarkus и R на производственном уровне. Пример, представленный в этой лекции, был специально разработан, чтобы показать понятный пример, вы не ограничены простыми функциями и вычислениями.

Обратите внимание, однако, что наш пример еще не готов к производству, и есть еще над чем поработать, это тема второй статьи 😉

Вы когда-нибудь интегрировали любые два языка друг с другом без использования отдыха или связи через сокеты? Насколько это было тяжело? Хотелось бы обменяться опытом по этой теме 🙂

Первоначально опубликовано на https://quarkify.net 23 апреля 2020 г.