调用 `ActorSystem.create` 时出现“Actor 名称 [用户] 不唯一”

问题描述 投票:0回答:1

背景

我正在尝试使用现有的 Java with Gradle 项目并使其使用 Akka Actor。当我创建演员系统时出现错误。这个错误似乎只在我使用 gradle 运行代码时才会发生(但不是 intelij,稍后会详细介绍)。该代码只是创建一个新的 ActorSystem,如文档中的示例所示。下面是整个main方法。

我的代码

public final class Main {
  private Main() {}
  public static void main(String... args) {

    var a = ActorSystem.<String>create(Behaviors.logMessages(Behaviors.ignore()), "Machine");
    // the code fails on the above line, before anything else can happen

    System.out.print("\n".repeat(5));

    a.tell("hello");

    try {
      Thread.sleep(10);
    } catch (InterruptedException e) {
      throw new RuntimeException(e);
    }
    System.out.print("\n".repeat(5));

    a.terminate();
  }
}

控制台输出

当我使用项目的运行任务运行上述代码时,我得到以下输出和错误。

[WARN] [01/05/2025 13:14:04.410] [Machine-akka.actor.internal-dispatcher-2] [CoordinatedShutdown(akka://Machine)] Task [terminate-system] in phase [actor-system-terminate] threw an exception before its future could be constructed: actor name [user] is not unique!

这是完整的错误和堆栈跟踪:

1:30:36 PM: Executing ':frc.robot.Main.main()'…


> Task :eventDeploy
Not running deploy task, skipping commit

> Task :createVersionFile
createVersionFile called. Path /[projectroot]/src/main/java/frc/robot

> Task :compileJava

> Task :processResources UP-TO-DATE
> Task :classes
warning: Supported source version 'RELEASE_17' from annotation processor 'org.gradle.api.internal.tasks.compile.processing.TimeTrackingProcessor' less than -source '22'
1 warning

> Task :frc.robot.Main.main()
[2025-01-05 13:30:38,133] [INFO] [akka.event.slf4j.Slf4jLogger] [Machine-akka.actor.default-dispatcher-4] [] - Slf4jLogger started
[2025-01-05 13:30:38,139] [WARN] [akka.actor.ActorSystemImpl] [Machine-akka.actor.default-dispatcher-4] [akka.actor.ActorSystemImpl(Machine)] - Dev use only. Free keys at https://akka.io/key










[2025-01-05 13:30:38,182] [INFO] [akka.actor.CoordinatedShutdown] [Machine-akka.actor.default-dispatcher-4] [CoordinatedShutdown(akka://Machine)] - Running CoordinatedShutdown with reason [ActorSystemTerminateReason]
[Incubating] Problems report is available at: file:///[projectroot]/build/reports/problems/problems-report.html

BUILD SUCCESSFUL in 1s
5 actionable tasks: 4 executed, 1 up-to-date
1:30:38 PM: Execution finished ':frc.robot.Main.main()'.

有趣的是,它不会因错误而停止代码。代码继续“运行”,但没有过去

ActorSystem.create(...)

我怀疑这是项目的 gradle 构建系统的问题,因为当我通过 Intelij 的运行按钮而不是项目的 gradle 任务运行代码时,它运行得很好并产生以下输出:

1:54:41 PM: Executing ':frc.robot.Main.main()'…


> Task :eventDeploy
Not running deploy task, skipping commit

> Task :createVersionFile
createVersionFile called. Path /[projectroot]/src/main/java/frc/robot

> Task :compileJava UP-TO-DATE
> Task :processResources UP-TO-DATE
> Task :classes UP-TO-DATE

> Task :frc.robot.Main.main()
[2025-01-05 13:54:41,812] [INFO] [akka.event.slf4j.Slf4jLogger] [Machine-akka.actor.default-dispatcher-4] [] - Slf4jLogger started
[2025-01-05 13:54:41,818] [WARN] [akka.actor.ActorSystemImpl] [Machine-akka.actor.default-dispatcher-4] [akka.actor.ActorSystemImpl(Machine)] - Dev use only. Free keys at https://akka.io/key










[2025-01-05 13:54:41,861] [INFO] [akka.actor.CoordinatedShutdown] [Machine-akka.actor.default-dispatcher-4] [CoordinatedShutdown(akka://Machine)] - Running CoordinatedShutdown with reason [ActorSystemTerminateReason]

BUILD SUCCESSFUL in 628ms
5 actionable tasks: 3 executed, 2 up-to-date
1:54:41 PM: Execution finished ':frc.robot.Main.main()'.

这太棒了。它根本不记录“你好”,这不太好,但我可以处理这个问题。

想法

我对 akka 的了解还不够,无法理解为什么它会两次尝试为

user
actor 保留地址,但仅限于某些情况。

我的gradle.build

gradle 项目是一个 FRC 项目,因此复制它需要安装 WPILIB 并且需要一些努力,所以我不建议尝试复制它,但如果你愿意,这里是 zip 我基于该项目,下面是我的 build.gradle,这是我使用的 WPILIB 安装程序。我使用的是 MacOS 15.2。

import edu.wpi.first.gradlerio.GradleRIOPlugin

plugins {
    id "java"
    id "idea"
    id "application"
    id "edu.wpi.first.GradleRIO" version "2025.1.1-beta-2"
    id "com.peterabeles.gversion" version "1.10"
//    id "com.diffplug.spotless" version "6.12.0"
}

java {
    sourceCompatibility = JavaVersion.VERSION_22
    targetCompatibility = JavaVersion.VERSION_22
}

def ROBOT_MAIN_CLASS = "frc.robot.Main"

// Define my targets (RoboRIO) and artifacts (deployable files)
// This is added by GradleRIO's backing project DeployUtils.
deploy {
    targets {
        roborio(getTargetTypeClass('RoboRIO')) {
            // Team number is loaded either from the .wpilib/wpilib_preferences.json
            // or from command line. If not found an exception will be thrown.
            // You can use getTeamOrDefault(team) instead of getTeamNumber if you
            // want to store a team number in this file.
            team = project.frc.getTeamNumber()
            debug = project.frc.getDebugOrDefault(false)

            artifacts {
                // First part is artifact name, 2nd is artifact type
                // getTargetTypeClass is a shortcut to get the class type using a string

                frcJava(getArtifactTypeClass('FRCJavaArtifact')) {
                    jvmArgs.add("-XX:+UnlockExperimentalVMOptions")
                    jvmArgs.add("-XX:GCTimeRatio=5")
                    jvmArgs.add("-XX:+UseSerialGC")
                    jvmArgs.add("-XX:MaxGCPauseMillis=50")

                    // The options below may improve performance, but should only be enabled on the RIO 2
                    //
                    // final MAX_JAVA_HEAP_SIZE_MB = 100;
                    // jvmArgs.add("-Xmx" + MAX_JAVA_HEAP_SIZE_MB + "M")
                    // jvmArgs.add("-Xms" + MAX_JAVA_HEAP_SIZE_MB + "M")
                    // jvmArgs.add("-XX:+AlwaysPreTouch")
                }

                // Static files artifact
                frcStaticFileDeploy(getArtifactTypeClass('FileTreeArtifact')) {
                    files = project.fileTree('src/main/deploy')
                    directory = '/home/lvuser/deploy'
                    // Change to true to delete files on roboRIO that no
                    // longer exist in deploy directory on roboRIO
                    deleteOldFiles = false
                }
            }
        }
    }
}

def deployArtifact = deploy.targets.roborio.artifacts.frcJava

// Set to true to use debug for JNI.
wpi.java.debugJni = false

// Set this to true to enable desktop support.
def includeDesktopSupport = true

// Configuration for AdvantageKit
repositories {
    maven {
        url = uri("https://maven.pkg.github.com/Mechanical-Advantage/AdvantageKit")
        credentials {
            username = "Mechanical-Advantage-Bot"
            password = "\u0067\u0068\u0070\u005f\u006e\u0056\u0051\u006a\u0055\u004f\u004c\u0061\u0079\u0066\u006e\u0078\u006e\u0037\u0051\u0049\u0054\u0042\u0032\u004c\u004a\u006d\u0055\u0070\u0073\u0031\u006d\u0037\u004c\u005a\u0030\u0076\u0062\u0070\u0063\u0051"
        }
    }
    maven {
        url "https://repo.akka.io/maven"
    }
    mavenCentral()
    mavenLocal()
}

task(replayWatch, type: JavaExec) {
    mainClass = "org.littletonrobotics.junction.ReplayWatch"
    classpath = sourceSets.main.runtimeClasspath
}

def versions = [
    ScalaBinary: "2.13"
]


// Defining my dependencies. In this case, WPILib (+ friends), and vendor libraries.
// Also defines JUnit 4.
dependencies {
    annotationProcessor wpi.java.deps.wpilibAnnotations()
    implementation wpi.java.deps.wpilib()
    implementation wpi.java.vendor.java()

    roborioDebug wpi.java.deps.wpilibJniDebug(wpi.platforms.roborio)
    roborioDebug wpi.java.vendor.jniDebug(wpi.platforms.roborio)

    roborioRelease wpi.java.deps.wpilibJniRelease(wpi.platforms.roborio)
    roborioRelease wpi.java.vendor.jniRelease(wpi.platforms.roborio)

    nativeDebug wpi.java.deps.wpilibJniDebug(wpi.platforms.desktop)
    nativeDebug wpi.java.vendor.jniDebug(wpi.platforms.desktop)
    simulationDebug wpi.sim.enableDebug()

    nativeRelease wpi.java.deps.wpilibJniRelease(wpi.platforms.desktop)
    nativeRelease wpi.java.vendor.jniRelease(wpi.platforms.desktop)
    simulationRelease wpi.sim.enableRelease()

    testImplementation 'org.junit.jupiter:junit-jupiter:5.10.1'
    testRuntimeOnly 'org.junit.platform:junit-platform-launcher'

    def akitJson = new groovy.json.JsonSlurper().parseText(new File(projectDir.getAbsolutePath() + "/vendordeps/AdvantageKit.json").text)


    annotationProcessor "org.littletonrobotics.akit:akit-autolog:$akitJson.version"

    implementation platform("com.typesafe.akka:akka-bom_${versions.ScalaBinary}:2.10.0")

    implementation "com.typesafe.akka:akka-actor-typed_${versions.ScalaBinary}"
    testImplementation "com.typesafe.akka:akka-actor-testkit-typed_${versions.ScalaBinary}"

    annotationProcessor "org.jetbrains:annotations:24.0.0"
    implementation "org.jetbrains:annotations:24.0.0"

    implementation "ch.qos.logback:logback-classic:1.5.6"
}

test {
    useJUnitPlatform()
    systemProperty 'junit.jupiter.extensions.autodetection.enabled', 'true'
}

// Simulation configuration (e.g. environment variables).
//
// The sim GUI is *disabled* by default to support running
// AdvantageKit log replay from the command line. Set the
// value to "true" to enable the sim GUI by default (this
// is the standard WPILib behavior).
wpi.sim.addGui().defaultEnabled = true
wpi.sim.addDriverstation()

// Setting up my Jar File. In this case, adding all libraries into the main jar ('fat jar')
// in order to make them all available at runtime. Also adding the manifest so WPILib
// knows where to look for our Robot Class.
jar {
    from { configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } }
    from sourceSets.main.allSource
    manifest GradleRIOPlugin.javaManifest(ROBOT_MAIN_CLASS)
    duplicatesStrategy = DuplicatesStrategy.INCLUDE
}

// Configure jar and deploy tasks
deployArtifact.jarTask = jar
wpi.java.configureExecutableTasks(jar)
wpi.java.configureTestTasks(test)

// Configure string concat to always inline compile
tasks.withType(JavaCompile) {
    options.compilerArgs.add '-XDstringConcat=inline'
}

// Create version file
project.compileJava.dependsOn(createVersionFile)
gversion {
    srcDir       = "src/main/java/"
    classPackage = "frc.robot"
    className    = "BuildConstants"
    dateFormat   = "yyyy-MM-dd HH:mm:ss z"
    timeZone     = "America/New_York"
    indent       = "  "
}

// Create commit with working changes on event branches
task(eventDeploy) {
    doLast {
        if (project.gradle.startParameter.taskNames.any({ it.toLowerCase().contains("deploy") })) {
            def branchPrefix = "event"
            def branch = 'git branch --show-current'.execute().text.trim()
            def commitMessage = "Update at '${new Date().toString()}'"

            if (branch.startsWith(branchPrefix)) {
                exec {
                    workingDir(projectDir)
                    executable 'git'
                    args 'add', '-A'
                }
                exec {
                    workingDir(projectDir)
                    executable 'git'
                    args 'commit', '-m', commitMessage
                    ignoreExitValue = true
                }

                println "Committed to branch: '$branch'"
                println "Commit message: '$commitMessage'"
            } else {
                println "Not on an event branch, skipping commit"
            }
        } else {
            println "Not running deploy task, skipping commit"
        }
    }
}
createVersionFile.dependsOn(eventDeploy)

//// Spotless formatting
//project.compileJava.dependsOn(spotlessApply)
//spotless {
//    java {
//        target fileTree(".") {
//            include "**/*.java"
//            exclude "**/build/**", "**/build-*/**"
//        }
//        toggleOffOn()
//        googleJavaFormat()
//        removeUnusedImports()
//        trimTrailingWhitespace()
//        endWithNewline()
//    }
//    groovyGradle {
//        target fileTree(".") {
//            include "**/*.gradle"
//            exclude "**/build/**", "**/build-*/**"
//        }
//        greclipse()
//        indentWithSpaces(4)
//        trimTrailingWhitespace()
//        endWithNewline()
//    }
//    json {
//        target fileTree(".") {
//            include "**/*.json"
//            exclude "**/build/**", "**/build-*/**"
//        }
//        gson().indentWithSpaces(2)
//    }
//    format "misc", {
//        target fileTree(".") {
//            include "**/*.md", "**/.gitignore"
//            exclude "**/build/**", "**/build-*/**"
//        }
//        trimTrailingWhitespace()
//        indentWithSpaces(2)
//        endWithNewline()
//    }
//}

我的研究

我检查了谷歌,有很多线程讨论重复的演员,但没有一个关于

ActorSystem.create(...)
失败的线程,因为它试图创建多个
user
演员。

我尝试过的

我已经尝试过了

  1. 使用他们推荐的java版本和发行版,不同版本
  2. 将默认的
    application.conf
    添加到资源文件夹中(这似乎确实有效,因为当我犯了这个错误时,它引发了一个新错误)。

由于在我开始添加之前该项目没有使用或没有 Akka,因此该项目与 Akka 冲突是没有意义的。

我对 akka 的经验还不够,无法识别此错误或了解可能导致该错误的原因。

我的问题

什么可能导致此问题?我意识到,如果没有太多可做的事情,诊断这个问题是很困难的,但我只需要线索来了解可能导致这种情况的原因。一旦我了解了两种运行方式之间的具体区别,我就可以更改一些内容以使其正常工作。

java gradle akka actor akka-actor
1个回答
0
投票

感谢您分享详细的步骤,帮助我在本地轻松设置 FRC 项目以进行调试。以下是我的观察:

  1. 项目使用以下基础设施运行:

  2. 为 VS Code 配置 WPI 扩展后,安装必要的依赖项,如下所示 enter image description here

  3. 导入项目并使用以下运行命令构建 gradle 以检查 RioLog。 enter image description here

  4. 通过使用 akka [actor 模型系统依赖项] 和 HelloWorld lightbend 示例扩展项目来重新运行相同的 gradle。 enter image description here

更新代码可在 GitHub 中获取,供我们参考。

注意:请使用提供的 build.gradle 文件进行测试@a1cd,并让我知道是否需要澄清任何内容。当我运行你的 build.gradle 时,我遇到了一些 jdk 差异,我预计这种差异可能会遇到你的 actor 系统未运行的情况。

谢谢 法鲁克

© www.soinside.com 2019 - 2024. All rights reserved.