我正在尝试使用现有的 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 项目是一个 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
演员。
我已经尝试过了
application.conf
添加到资源文件夹中(这似乎确实有效,因为当我犯了这个错误时,它引发了一个新错误)。由于在我开始添加之前该项目没有使用或没有 Akka,因此该项目与 Akka 冲突是没有意义的。
我对 akka 的经验还不够,无法识别此错误或了解可能导致该错误的原因。
什么可能导致此问题?我意识到,如果没有太多可做的事情,诊断这个问题是很困难的,但我只需要线索来了解可能导致这种情况的原因。一旦我了解了两种运行方式之间的具体区别,我就可以更改一些内容以使其正常工作。
感谢您分享详细的步骤,帮助我在本地轻松设置 FRC 项目以进行调试。以下是我的观察:
项目使用以下基础设施运行:
通过使用 akka [actor 模型系统依赖项] 和 HelloWorld lightbend 示例扩展项目来重新运行相同的 gradle。 。
更新代码可在 GitHub 中获取,供我们参考。
注意:请使用提供的 build.gradle 文件进行测试@a1cd,并让我知道是否需要澄清任何内容。当我运行你的 build.gradle 时,我遇到了一些 jdk 差异,我预计这种差异可能会遇到你的 actor 系统未运行的情况。
谢谢 法鲁克