Grpc.Core.RpcException方法在C#客户端和Java服务器中未实现。

19

我在寻找这个错误的源头时遇到了麻烦。我使用 Protocol Buffers 实现了一个简单的服务:

syntax = "proto3";

package tourism;

service RemoteService {
  rpc Login(LoginUserDTO) returns (Response) {}
}

message AgencyDTO{
  int32 id=1;
  string name=2;
  string email=3;
  string password=4;
}

message LoginUserDTO{
  string password=1;
  string email=2;
}

message SearchAttractionsDTO{
  string name=1;
  int32 start_hour=2;
  int32 start_minute=3;
  int32 stop_hour=4;
  int32 stop_minute=5;
  AgencyDTO loggedUser=6;
}

message AttractionDTO{
  int32 id=1;
  string name=2;
  string agency=3;
  int32 hour=4;
  int32 minute=5;
  int32 seats=6;
  int32 price=7;
}

message ReservationDTO{
  int32 id=1;
  string first_name=2;
  string last_name=3;
  string phone=4;
  int32 seats=5;
  AttractionDTO attraction=6;
  AgencyDTO agency=7;
}

message Response{
  enum ResponseType{
    OK=0;
    NOT_LOGGED_ID=1;
    SERVER_ERROR=2;
    VALIDATOR_ERROR=3;
  }
  ResponseType type=1;
  AgencyDTO user=2;
  string message=3;
}

使用Java客户端时,一切正常,服务器接收请求并作出适当响应。但是,当我使用相同的.proto文件用于在C#中生成源代码,并调用client.Login()时,我会遇到以下错误:Grpc.Core.RpcException Status(StatusCode=Unimplemented, Detail="Method tourism.RemoteService/Login is unimplemented")。服务器会接收请求,但没有时间作出响应并抛出:

INFO: Request from ex@ex.com
May 22, 2017 12:28:58 AM io.grpc.internal.SerializingExecutor run
SEVERE: Exception while executing runnable io.grpc.internal.ServerImpl$JumpToApplicationThreadServerStreamListener$2@4be43082
java.lang.IllegalStateException: call is closed
    at com.google.common.base.Preconditions.checkState(Preconditions.java:174)
    at io.grpc.internal.ServerCallImpl.sendHeaders(ServerCallImpl.java:103)
    at io.grpc.stub.ServerCalls$ServerCallStreamObserverImpl.onNext(ServerCalls.java:282)
    at ServiceImp.login(ServiceImp.java:20)
    at tourism.RemoteServiceGrpc$MethodHandlers.invoke(RemoteServiceGrpc.java:187)
    at io.grpc.stub.ServerCalls$1$1.onHalfClose(ServerCalls.java:148)
    at io.grpc.internal.ServerCallImpl$ServerStreamListenerImpl.halfClosed(ServerCallImpl.java:262)
    at io.grpc.internal.ServerImpl$JumpToApplicationThreadServerStreamListener$2.runInContext(ServerImpl.java:572)
    at io.grpc.internal.ContextRunnable.run(ContextRunnable.java:52)
    at io.grpc.internal.SerializingExecutor.run(SerializingExecutor.java:117)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)

Java服务器:

import io.grpc.Server;
import io.grpc.ServerBuilder;
import io.grpc.stub.StreamObserver;
import tourism.RemoteServiceGrpc;
import tourism.Service;

import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * Created by Andu on 21/05/2017.
 */
public class ServerGrpc {
    Logger logger= Logger.getLogger(ServerGrpc.class.getName());
    private final Server server;
    private final int port;

    public ServerGrpc(int p){
        port=p;
        server= ServerBuilder.forPort(port).addService(new ServiceImp()).build();
    }

    public void start() throws IOException {
        server.start();
        logger.info("Server started, listening on " + port);
        Runtime.getRuntime().addShutdownHook(new Thread() {
            @Override
            public void run() {
                // Use stderr here since the logger may has been reset by its JVM shutdown hook.
                System.err.println("*** shutting down gRPC server since JVM is shutting down");
                ServerGrpc.this.stop();
                System.err.println("*** server shut down");
            }
        });
    }

    public void stop() {
        if (server != null) {
            server.shutdown();
        }
    }

    void blockUntilShutdown() throws InterruptedException {
        if (server != null) {
            server.awaitTermination();
        }
    }

    private class ServiceImp extends RemoteServiceGrpc.RemoteServiceImplBase {
        Logger log=Logger.getLogger(ServiceImp.class.getName());

        @Override
        public void login(Service.LoginUserDTO request, StreamObserver<Service.Response> responseStreamObserver){
            super.login(request,responseStreamObserver);
            log.log(Level.INFO,"Request from "+request.getEmail());
            Service.Response response= Service.Response.newBuilder().setMessage("Hello "+request.getEmail()+", I know your password: "+request.getPassword()).build();
            responseStreamObserver.onNext(response);
            responseStreamObserver.onCompleted();
        }
    }

}

C# 客户端:

namespace testGrpc2
{
    class MainClass
    {
        public static void Main(string[] args)
        {
            var channel = new Channel("127.0.0.1:61666",ChannelCredentials.Insecure);
            var client = new RemoteService.RemoteServiceClient(channel);
            Response response=client.Login(new LoginUserDTO{Email="ex@ex.com",Password="notmypassword"});
            Console.WriteLine(response);
            Console.ReadKey();
        }
    }
} 
6个回答

31

对我来说,问题是我忘记在启动类中添加gRpc服务的端点。

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        app.UseRouting();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapGrpcService<GreeterService>();

            //Add your endpoint here like this
            endpoints.MapGrpcService<YourProtoService>();
        });
    

这正是我的问题,我已将其分为两个不同的协议,并忘记注册新的协议。 - Heitor Corrêa
噢呵呵,我是个新手!这是我的新手失误。谢谢。 - granadaCoder
app.MapGrpcService<GreeterService>();app.MapGrpcService<ProductsAppService>(); - iarunpaul

30

我找到了问题的根源。对于其他遇到这个问题的人:

请确保你的.proto文件在客户端和服务器端是相同的,并且它们有相同的包名。当客户端调用远程服务器上的方法时,它使用远程类的完整名称和包名。

但是这不是方法在客户端中显示为未实现的原因。原因是:

super.login(request,responseStreamObserver);

调用父类的login方法会向客户端发送异步的UNIMPLEMENTED错误代码。这是生成类中的login()方法:

public void login(LoginUserDTO request,StreamObserver<Response> responseObserver) {
          asyncUnimplementedUnaryCall(METHOD_LOGIN, responseObserver);
}
所以,在实现您的服务方法时,请确保不要调用super方法,因为它会对客户端显示为未实现。如果您使用IntelliJ IDEA生成@Override方法,它将添加super方法调用。请务必删除它。

对于那些需要更多关于Java的信息的人,请查看 https://grpc.io/docs/reference/java/generated-code/。 - techkuz
1
我在C#服务器/客户端中遇到了同样的问题。最初没有想到Java中的超类相当于C#中的基类。几个小时后第二次查看时,一切都变得清晰。谢谢! - Francis

6
对于我来说,使用 C# 客户端的问题在于我没有重写生成的服务方法。

1
上帝,谢谢您。 - Rodolfo Luna

1
感谢Alexandru的帮助,这有助于解决我在使用Akka gRPC客户端和Python gRPC服务器时遇到的问题。在我的情况下,.proto文件中具有相同的包和前言,但已经消除了用不到的消息类和gRPC函数。当我使.proto文件相同时,一切正常,我不再收到UNIMPLEMENTED错误。这对于超出C# / Java示例之外的语言是必需的。再次感谢。

0

我的服务器和客户端都是Java编写的,在IntelliJ中关闭并重新打开项目后,问题已经解决了!


0
在我的情况下,问题出现在服务器端。
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddGrpc();

builder.Services.AddMemoryCache();

builder.Logging.AddConsole();

//this 3 lines was missed (before builder.Build()):

    builder.Services.AddCodeFirstGrpc(config =>
    {
     config.ResponseCompressionLevel = System.IO.Compression.CompressionLevel.Optimal;
    });

var app = builder.Build();

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接