1

我计划使用 FastR 实现编写一个 R 脚本,看起来像

    java.addToClasspath("build/libs/polyglot-example-f.jar")
    clientClass <- java.type('org.algo.polyglot.JavaClient')
    javaClient <- new(clientClass, threadPoolSize=10)
    javaClient$startScheduleWithFixedDelay(5)

该类org.algo.polyglot.JavaClient看起来像(打包到一个 jar 中):

    package org.algo.polyglot;

    import org.apache.commons.lang3.RandomStringUtils;
    import org.apache.commons.lang3.RandomUtils;
    import org.graalvm.polyglot.HostAccess;

    import java.text.MessageFormat;
    import java.util.concurrent.Executor;
    import java.util.concurrent.Executors;
    import java.util.concurrent.ScheduledExecutorService;
    import java.util.concurrent.TimeUnit;

    import org.graalvm.polyglot.HostAccess.Export;

    public class JavaClient {

        private final ScheduledExecutorService service;

        @Export
        public JavaClient(int threadPoolSize) {
            this.service = Executors.newScheduledThreadPool(threadPoolSize);
        }

        @Export
        public void startScheduleWithFixedDelay(int delayInSeconds) {
            service.scheduleWithFixedDelay(
                    () -> {
                        postMessage(fetchMessage());
                    },
                    0,
                    delayInSeconds,
                    TimeUnit.SECONDS
            );
        }

        @Export
        public void startScheduleWithFixedRate(int period) {
            service.scheduleAtFixedRate(
                    () -> {},
                    0,
                    period,
                    TimeUnit.SECONDS
            );
        }

        @Export
        public PolyglotMessage fetchMessage() {
            return new PolyglotMessage(
                    RandomStringUtils.randomAlphabetic(10, 15),
                    RandomStringUtils.randomAlphabetic(10, 15),
                    System.currentTimeMillis()
            );
        }

        @Export
        public void postMessage(PolyglotMessage message) {
            System.out.println("[Printed from JavaClient] Posting message: " + message.toString());
        }
    }

运行脚本时Rscript sample.R --polyglot,脚本完成执行并输出:

    $ Rscript sample.R --polyglot
    NULL

我有这些动机:

  • R 脚本应该是入口点,因为我的用例要求我利用 R 中的 java 功能
  • 使用提供的 jar 运行 R 脚本
  • 使用 Rscript 执行脚本
  • ScheduledExecutorService没有 R 脚本完成执行的情况下从 R 脚本本身以固定延迟或固定速率运行(保持活动状态直到服务运行)这可以通过返回预定的未来并等待它使用ScheduledFuture$get()对脚本所做的更改来解决和代码是:
    future <- javaClient$startScheduleWithFixedDelay(5)
    future$get()
    @Export
    public ScheduledFuture<?> startScheduleWithFixedDelay(int delayInSeconds) {
        return service.scheduleWithFixedDelay(
                () -> {
                    postMessage(fetchMessage());
                },
                0,
                delayInSeconds,
                TimeUnit.SECONDS
        );
    }
  • 将一个函数从 R 脚本传递给执行器,以便 ScheduledExecutorService 相应地调用该函数(可以传递一个 R 函数以在 java 代码中运行,接收器将是一个功能接口,例如:)

    myFunc <- function() {
        # do some stuff
    }

javaClient$runProvidedFunction(myFunc)

    @Export
    public void runProvidedFunction(Runnable runnable) {
        runnable.run();
    }

但是来自不同线程的访问受到限制

  • 使用可运行脚本和 jar 构建本机映像

我想知道这些动机是否可能,如果可能的话,这样做的正确方法。

4

1 回答 1

1

使用提供的 jar 运行 R 脚本

看来你自己想通了。在启动 R/Rscript 时,您可以使用java.addToClasspath或者应该能够添加到类路径中--vm.cp=...。这两个选项仅在 JVM 模式下有效。

使用 Rscript 执行脚本

再一次,你似乎明白了吗?

将函数从 R 脚本传递给执行程序,以便 ScheduledExecutorService 相应地调用该函数

这很棘手。R 是单线程语言,FastR 也是。您不能从多个线程执行 R 代码,但您可以做的是创建多个 FastR 上下文并在每个线程中使用一个新上下文(您可以为此使用 ThreadLocal)。请参阅https://www.graalvm.org/reference-manual/embed-languages。您应该能够使用从 R 调用的 Java 代码中的 Context API 并创建新的上下文。

使用可运行脚本和 jar 构建本机映像

那应该没问题。请注意,一些 R 包和本机映像存在一些我们尚未修复的已知问题。

入口点必须是一些使用 Context API 来启动 FastR 的 Java 应用程序。您可以将 R 脚本作为字符串或资源嵌入到 Java 应用程序中。请注意,不会提前编译 R 代码。它仍将在运行时进行 JIT 编译,但其他所有内容都将提前编译:您的 Java 代码、您使用的任何 Java 库、FastR 解释器和运行时。

于 2021-12-31T09:35:25.267 回答