我有简单的 grpc 客户端和服务器。
public SensorRPCServer(ShareReadingsService service, int port) {
this.service = service;
this.port = port;
}
public void start() {
try {
server = ServerBuilder.forPort(port)
.addService(service)
.build()
.start();
logger.info("Server started on " + port);
// Clean shutdown of server in case of JVM shutdown
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
logger.severe("Shutting down gRPC server since JVM is shutting down");
SensorRPCServer.this.stop();
}));
} catch (IOException e) {
logger.severe("GRPC server could not start up.");
}
}
private static final Logger logger = Logger.getLogger(SensorRPCClient.class.getName());
private final ManagedChannel channel;
private final ShareReadingsGrpc.ShareReadingsBlockingStub blockingStub;
public SensorRPCClient(String name, int port) {
this.channel = ManagedChannelBuilder.forAddress(name, port).usePlaintext().build();
blockingStub = ShareReadingsGrpc.newBlockingStub(channel);
}
客户端和服务端在同一个项目中。意思是传感器是客户端(获取其他传感器数据)和服务器(向其他传感器提供读数)。
我从 ubuntu 终端构建并运行 java 项目,并传递 grpc 服务器运行的端口。
构建.gradle:
plugins {
id 'java'
id 'com.github.johnrengelman.shadow' version '8.1.1' // Add this line
id 'com.google.protobuf' version '0.9.4'
}
group = 'labos1.labosi'
version = '1.0-SNAPSHOT'
repositories {
mavenCentral()
}
dependencies {
implementation 'org.json:json:20240303'
implementation 'com.opencsv:opencsv:5.9'
implementation 'io.grpc:grpc-netty-shaded:1.68.0'
implementation 'io.grpc:grpc-protobuf:1.68.0'
implementation 'io.grpc:grpc-services:1.68.0'
implementation 'io.grpc:grpc-stub:1.68.0'
implementation 'javax.annotation:javax.annotation-api:1.3.2'
testImplementation platform('org.junit:junit-bom:5.10.0')
testImplementation 'org.junit.jupiter:junit-jupiter'
}
test {
useJUnitPlatform()
}
shadowJar {
manifest {
attributes(
'Main-Class': 'labos1.labosi.Main' // Change this to your main class
)
}
}
sourceSets {
main {
java {
srcDirs 'build/generated/source/proto/main/grpc'
srcDirs 'build/generated/source/proto/main/java'
}
}
}
protobuf {
protoc {
artifact = "com.google.protobuf:protoc:3.25.5"
}
plugins {
grpc {
artifact = 'io.grpc:protoc-gen-grpc-java:1.68.0'
}
}
generateProtoTasks {
all()*.plugins {
grpc {}
}
all().each {
it.group = 'build'
}
}
}
我跑步
如果我从 intelij 启动第二个实例,它会连接到 3000 上的 grpc 服务器并且工作正常。
但是如果我从终端启动第二个实例 java -jar build/libs/sensor-1.0-SNAPSHOT-all.jar 3001
当 grpc 客户端尝试连接到 3000 上的 grpc 服务器时,它失败了。
INFO: Neighbour found: {"port":3000,"latitude":45.77,"ip":"127.0.0.1","longitude":15.96}
Exception in thread "main" java.lang.IllegalArgumentException: Address types of NameResolver 'unix' for '127.0.0.1:3000' not supported by transport
public SensorRPCClient(String host, int port) {
this.channel = ManagedChannelBuilder.forAddress(host, port).usePlaintext().build(); <-- breaks here
blockingStub = ShareReadingsGrpc.newBlockingStub(channel);
}
当 gRPC 无法正确解析提供的地址时,通常会发生 IllegalArgumentException 错误(“传输不支持 NameResolver 'unix' 的地址类型 'unix' for '127.0.0.1:3000'”),特别是在多实例设置中使用 localhost 和某些网络配置时。以下是解决此问题的一些解决方案:
按如下方式更新您的客户端实例化:
public SensorRPCClient(String host, int port) {
this.channel = ManagedChannelBuilder.forAddress("localhost", port)
.usePlaintext()
.build();
blockingStub = ShareReadingsGrpc.newBlockingStub(channel);
} 2. 检查端口冲突和防火墙 确保每个实例确实绑定到单独的端口而不发生冲突。确认没有防火墙或网络安全设置阻止两个实例通过本地主机进行通信。
客户端关闭示例
public void shutdown() throws InterruptedException {
channel.shutdown().awaitTermination(5, TimeUnit.SECONDS);
}
服务器关闭挂钩更新 确保服务器中的关闭挂钩设置正确,以避免端口绑定问题:
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
logger.severe("Shutting down gRPC server since JVM is shutting down");
try {
server.shutdown().awaitTermination(5, TimeUnit.SECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
})); 4.检查Gradle构建配置 如果项目在 IntelliJ 中运行良好但在终端中失败,则可能是由于构建配置的差异造成的。通过刷新 Gradle 依赖项并确认 .proto 文件已正确生成,确保终端和 IDE 构建保持一致。
用于故障排除的示例代码调整 实施上述建议后,您的代码可能如下所示:
public SensorRPCClient(String host, int port) {
this.channel = ManagedChannelBuilder.forAddress("localhost", port) // or use dns://
.usePlaintext()
.build();
blockingStub = ShareReadingsGrpc.newBlockingStub(channel);
}
完成这些调整后,尝试再次运行第二个实例,它应该可以解决 IllegalArgumentException 错误。