You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Ruby与C#的gRPC通信报错:服务未实现问题排查与解决

问题描述

我开发了一个C#版本的gRPC服务器,其proto文件定义如下:

package xxx.common;
option csharp_namespace = "Xxx.ComCon";

service PeerController {
...
}

对应的C#实现代码:

namespace Xxx.ComCon;

public partial class PeerControllerImpl : Peer { ... }

随后我将该proto文件复制到Ruby项目中,使用protoc生成了存根代码:

require 'grpc'
require 'PeerController_pb'

module Xxx
  module Common
    module PeerController

并编写了GrpcClient类:

class GrpcClient
    def initialize(peer)
      @stub = Xxx::Common::PeerController::Stub.new("#{peer.server.private_ip}:#{peer.port}", :this_channel_is_insecure)
    end
  
    def say_hello
      request = Xxx::Common::IdentifiedRequest.new(name: "RoR calling")
      response = @stub.say_hello(request)
      response.message
    end
  end

执行GrpcClient调用时,C#服务器抛出错误:

Service 'xxx.common.PeerController' is unimplemented.

我怀疑问题出在C#中命名空间Xxx.ComCon下的PeerControllerImpl实现,与Ruby调用方的xxx.common.PeerController命名空间不匹配,是否确实如此?该如何解决这个问题?

我原本预期服务器能接收请求并返回消息,以下是SayHello方法的实现:

public override Task<SuccessReply> SayHello(IdentifiedRequest request, ServerCallContext context)
{
    var metadata = context.RequestHeaders;
    var clientId = metadata.GetValue("client-id");

    return Task.FromResult(new SuccessReply
    {
        Success = true,
        Message = $"Hello from PeerController to: " + request.Name,
    });
}
问题分析与解决

你的怀疑方向正确,问题核心是gRPC服务标识不匹配。

gRPC服务的唯一标识由proto定义的package + service name决定,也就是xxx.common.PeerController。但你的C#实现存在关键问题,导致服务器无法关联到正确的服务:

1. 错误的基类继承

当protoc为C#生成gRPC代码时,针对service PeerController会生成名为PeerControllerBase的抽象基类(而非Peer)。你的PeerControllerImpl继承Peer,会导致服务器无法识别该实现对应的是xxx.common.PeerController服务。

2. 解决步骤

  • 修正C#服务实现的继承关系
    确保你的实现继承自动生成的PeerControllerBase类:

    namespace Xxx.ComCon;
    
    public partial class PeerControllerImpl : PeerControllerBase 
    {
        public override Task<SuccessReply> SayHello(IdentifiedRequest request, ServerCallContext context)
        {
            var metadata = context.RequestHeaders;
            var clientId = metadata.GetValue("client-id");
    
            return Task.FromResult(new SuccessReply
            {
                Success = true,
                Message = $"Hello from PeerController to: " + request.Name,
            });
        }
    }
    
  • 确认服务器服务注册逻辑
    启动C# gRPC服务器时,必须使用生成的服务描述符绑定你的实现:

    var server = new Server
    {
        Services = { PeerController.BindService(new PeerControllerImpl()) },
        Ports = { new ServerPort("0.0.0.0", 50051, ServerCredentials.Insecure) }
    };
    server.Start();
    

    这里的PeerController是protoc生成的包含服务元数据的类,BindService方法会将你的实现与xxx.common.PeerController服务标识关联起来。

  • Ruby客户端无需修改
    Ruby生成的Xxx::Common::PeerController::Stub完全对应proto中的xxx.common.PeerController服务,这部分是正确的。

完成以上修改后,C#服务器就能正确识别Ruby客户端的请求,匹配到对应的服务实现,解决"服务未实现"的错误。

内容的提问来源于stack exchange,提问作者Rodrigo Botafogo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 14:35:15