Найти в Дзене
Один Rust не п...Rust

Rust внутри Java

Оглавление
nicktretyakov/ml-gpu-java-service

Для чего нужна данная статья? :

Научиться использованию инструментов, таких как GraalVM, для интеграции Rust внутри Java.

Вызывать функций на Rust из Java.

Создать распределённую систему машинного обучения с гибридным бэкендом на Rust и Java.

  • Rust: высокопроизводительный сервер для распределённых вычислений (gRPC + Tokio + CUDA).
  • Java: управление задачами через Spring Boot (REST API + WebSocket).
  • Функционал:Сервер (Rust) получает задачи и вычисляет нейросетевые модели на GPU.
    Java-приложение координирует задачи между клиентами и сервером.
    WebSocket уведомляет клиентов о статусе.
    Rust использует wgpu для GPU-ускорения.

Для чего Вам это уметь? :

Для использования Rust внутри Java с помощью GraalVM, SubstrateVM, которая является частью GraalVM и предоставляет возможность создания нативных образов.

Убедитесь, что у вас установлен GraalVM.

Создайте Rust-функцию, которую вы хотите вызвать из Java-кода.

Пример кода lib.rs:

#[no_mangle]
pub extern "C" fn add_numbers(a: i32, b: i32) -> i32 {
a + b
}

Сборка Rust-кода в библиотеку:

rustc --target wasm32-unknown-unknown --crate-type cdylib lib.rs

Java-код:

public class RustIntegrationExample {
static {
// Загрузка библиотеки Rust с использованием GraalVM
System.loadLibrary("path/to/your/rust/library");
}
// Объявление native-метода, который будет вызывать Rust-функцию
public native int addNumbers(int a, int b);
public static void main(String[] args) {
// Создание экземпляра класса
RustIntegrationExample example = new RustIntegrationExample();
// Вызов Rust-функции из Java
int result = example.addNumbers(5, 10);
System.out.println("Result: " + result);
}
}

Создание заголовочного файла для Java Native Interface (JNI):

javac -h . RustIntegrationExample.java

Компиляция Java-кода с использованием GraalVM:

native-image --no-fallback -H:+ReportUnsupportedElementsAtRuntime -jar your-java-jar-file.jar

Запуск нативного образа:

./your-java-jar-file

Гибридное решение для распределённых вычислений в машинном обучении

  • Spring Boot принимает REST-запрос на /api/ml/compute.
  • Java через gRPC отправляет данные в Rust-сервер.
  • Rust запускает GPU-вычисления и отправляет ответ обратно.
  • Java отправляет результаты клиентам через WebSocket.
  • Web-интерфейс получает уведомления о готовности результата.

1. Rust: gRPC-сервер с GPU-вычислениями

Функционал:

  • Получает данные для обучения нейросети.
  • Запускает вычисления на GPU (wgpu).
  • Отправляет результат через gRPC.

use tonic::{transport::Server, Request, Response, Status};

use tokio::sync::mpsc;

use prost::Message;

use wgpu::{Device, Queue, ShaderModule};

use std::sync::Arc;

// gRPC-сообщения

pub mod ml {

tonic::include_proto!("ml");

}

use ml::{ml_server::{Ml, MlServer}, ComputeRequest, ComputeResponse};

#[derive(Debug, Default)]

pub struct MlService {

device: Arc<Device>,

queue: Arc<Queue>,

shader: ShaderModule,

}

#[tonic::async_trait]

impl Ml for MlService {

async fn compute(

&self,

request: Request<ComputeRequest>,

) -> Result<Response<ComputeResponse>, Status> {

let data = request.into_inner().data;

let result = self.run_gpu_task(data).await;

Ok(Response::new(ComputeResponse { result }))

}

}

impl MlService {

async fn run_gpu_task(&self, data: Vec<f32>) -> Vec<f32> {

// Выполняем сложные вычисления на GPU

data.iter().map(|x| x * 2.0).collect() // (заглушка, заменить на wgpu)

}

}

#[tokio::main]

async fn main() -> Result<(), Box<dyn std::error::Error>> {

let addr = "[::1]:50051".parse()?;

let ml_service = MlService::default();

println!("ML Server running on {}", addr);

Server::builder()

.add_service(MlServer::new(ml_service))

.serve(addr)

.await?;

Ok(())

}

2. Java: Spring Boot клиент, REST API + WebSocket

Функционал:

  • Запрашивает у Rust-сервера выполнение задачи.
  • Поддерживает WebSocket для уведомлений.

2.1. gRPC-клиент для Rust

<!-- pom.xml -->

<dependency>

<groupId>io.grpc</groupId>

<artifactId>grpc-netty-shaded</artifactId>

<version>1.47.0</version>

</dependency>

<dependency>

<groupId>io.grpc</groupId>

<artifactId>grpc-protobuf</artifactId>

<version>1.47.0</version>

</dependency>

<dependency>

<groupId>io.grpc</groupId>

<artifactId>grpc-stub</artifactId>

<version>1.47.0</version>

</dependency>

2.2. Spring Boot REST API

import io.grpc.ManagedChannel;

import io.grpc.ManagedChannelBuilder;

import ml.ComputeRequest;

import ml.ComputeResponse;

import ml.MlGrpc;

import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController

@RequestMapping("/api/ml")

public class MLController {

private final MlGrpc.MlBlockingStub stub;

public MLController() {

ManagedChannel channel = ManagedChannelBuilder.forAddress("localhost", 50051)

.usePlaintext()

.build();

stub = MlGrpc.newBlockingStub(channel);

}

@PostMapping("/compute")

public List<Float> compute(@RequestBody List<Float> data) {

ComputeRequest request = ComputeRequest.newBuilder()

.addAllData(data)

.build();

ComputeResponse response = stub.compute(request);

return response.getResultList();

}

}

3. WebSocket-сервер для уведомлений

Функционал:

  • Отправляет клиентам результаты вычислений.

3.1. WebSocket-конфигурация

import org.springframework.context.annotation.Configuration;

import org.springframework.web.socket.config.annotation.*;

@Configuration

@EnableWebSocket

public class WebSocketConfig implements WebSocketConfigurer {

@Override

public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {

registry.addHandler(new WebSocketHandler(), "/ws");

}

}

3.2. WebSocket-обработчик

import org.springframework.web.socket.*;

import java.io.IOException;

import java.util.concurrent.CopyOnWriteArrayList;

public class WebSocketHandler extends TextWebSocketHandler {

private static final CopyOnWriteArrayList<WebSocketSession> sessions = new CopyOnWriteArrayList<>();

@Override

public void afterConnectionEstablished(WebSocketSession session) {

sessions.add(session);

}

public static void sendUpdate(String message) {

for (WebSocketSession session : sessions) {

try {

session.sendMessage(new TextMessage(message));

} catch (IOException e) {

e.printStackTrace();

}

}

}

}

3.3. Отправка сообщений при завершении вычислений

import org.springframework.scheduling.annotation.Scheduled;

import org.springframework.stereotype.Component;

import java.util.Random;

@Component

public class MLTaskScheduler {

@Scheduled(fixedRate = 5000)

public void simulateComputation() {

float result = new Random().nextFloat() * 100;

WebSocketHandler.sendUpdate("Computation completed: " + result);

}

}

4. Клиентский WebSocket (HTML + JS)

<!DOCTYPE html>

<html>

<head>

<title>ML Updates</title>

<script>

const ws = new WebSocket("ws://localhost:8080/ws");

ws.onmessage = function(event) {

document.getElementById("log").innerHTML += "<p>" + event.data + "</p>";

};

</script>

</head>

<body>

<h2>ML Task Updates</h2>

<div id="log"></div>

</body>

</html>