Service Fabric:System.ArgumentException:找不到具有此 ID 的接口 -488762776

Service Fabric: System.ArgumentException: No interface found with this Id -488762776

我正在开发一个简单的 Service Fabric 集群,我想在其中从无状态 ASP.NET Core 2.0 web API.

调用无状态服务

我做的第一件事是创建一个具有简单界面和 DTO 的 .NET Standard 2.0 class 库:

public interface IMicroService : IService
{
    Task<MyDto> GetMahDto(int id);
}

public class MyDto
{
    public string Name { get; set; }
}

然后我从中创建了一个 NuGet 包,并将该包作为依赖项添加到我的 Web API(MyServiceApi 项目)和无状态服务 (MyService)。

服务的侦听器定义为

        protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
    {
        var fabricListener = new ServiceInstanceListener((context) =>
        {
            var fabricRemotingListener = new FabricTransportServiceRemotingListener(
            serviceContext: context,
            serviceRemotingMessageHandler: null,
            remotingListenerSettings: new Microsoft.ServiceFabric.Services.Remoting.FabricTransport.Runtime.FabricTransportRemotingListenerSettings()
            {
                EndpointResourceName = "myendpoint"
            },
            serializationProvider: null);


            return fabricRemotingListener;
        },
       "mylistener");

        return new ServiceInstanceListener[] { fabricListener };
    }

接口实现简单

public Task<MyDto> GetMahDto(int id)
    {
        return Task.FromResult(new MyDto() { Name=$"Hallo from TheService in FabricSandbox3 project, id: {id}" });
    }

在 MyServiceApi 中,我有一个看起来像

的控制器方法
[HttpGet]
    public async Task<MyDto> Get()
    {

        var svc = ServiceProxy.Create<ContractsStandard.IMicroService>(new Uri("fabric:/FabricSandbox4/TheService"), listenerName: "mylistener");

        return await svc.GetMahDto(23);
    }

当我启动调试器时,我能够进入 MyServiceApi 的控制器方法,但它抛出以下异常:

System.AggregateException: '发生一个或多个错误。 (异常 System.ArgumentException 在服务上未处理,无法序列化以传输到客户端。 详细的远程异常信息:System.ArgumentException: No interface found with this Id -488762776 在 Microsoft.ServiceFabric.Services.Remoting.V2.ServiceRemotingMessageSerializersManager.GetInterfaceDetails(Int32 接口 ID) 在 Microsoft.ServiceFabric.Services.Remoting.V2.ServiceRemotingMessageSerializersManager.CreateSerializers(Int32 接口 ID) 在 System.Collections.Concurrent.ConcurrentDictionary2.GetOrAdd(TKey key, Func2 值工厂) 在 Microsoft.ServiceFabric.Services.Remoting.V2.ServiceRemotingMessageSerializersManager.GetRequestBodySerializer(Int32 接口 ID) 在 Microsoft.ServiceFabric.Services.Remoting.V2.FabricTransport.Runtime.FabricTransportMessageHandler.CreateRemotingRequestMessage(FabricTransportMessage fabricTransportMessage, Stopwatch 秒表) 在 Microsoft.ServiceFabric.Services.Remoting.V2.FabricTransport.Runtime.FabricTransportMessageHandler.d__7.MoveNext())'

我在我的诊断事件中没有发现任何看起来有希望的东西,而且我看到的唯一提到的类似问题(在这个站点和其他地方)通常涉及不同程序集中的类型,这不是问题假定共享接口包含在 NuGet 包中。

我有什么地方可能出错的想法吗?

我确实遇到过一次同样的问题,但无法解决,然后使用 NonIServiceProxy method:

public async Task<MyDto> Get()
{

    var svc = ServiceProxy.CreateNonIServiceProxy<ContractsStandard.IMicroService>(new Uri("fabric:/FabricSandbox4/TheService"), listenerName: "mylistener");

    return await svc.GetMahDto(23);
}

基本上它消除了在服务和客户端之间共享相同接口的要求。

查看此答案的底部以直接获得最终解决方案。

我找到了部分令人满意的答案。但是,我不完全知道它为什么起作用。我偶然发现了这个纯属偶然。我一直在研究一种无缝传递 headers 结构请求的方法,这就是我最终达到这个目标的方式。

参考上面的监听代码,特别注意这一行:

 serviceRemotingMessageHandler: null,

为了让它工作,我创建了自己的 IServiceRemotingMessageHandler 实现。请注意,它只是将调用委托给基本类型:

class TestRemotingDispatcher : ServiceRemotingMessageDispatcher, IServiceRemotingMessageHandler
{

    public TestRemotingDispatcher(
        ServiceContext serviceContext, IService serviceImplementation, IServiceRemotingMessageBodyFactory serviceRemotingMessageBodyFactory = null) :
        base(serviceContext, serviceImplementation, serviceRemotingMessageBodyFactory)
    {

    }

    public override void HandleOneWayMessage(IServiceRemotingRequestMessage requestMessage)
    {
        base.HandleOneWayMessage(requestMessage);
    }

    public override Task<IServiceRemotingResponseMessageBody> HandleRequestResponseAsync(
        ServiceRemotingDispatchHeaders requestMessageDispatchHeaders, IServiceRemotingRequestMessageBody requestMessageBody, CancellationToken cancellationToken)
    {

        return base.HandleRequestResponseAsync(requestMessageDispatchHeaders, requestMessageBody, cancellationToken);
    }

    public override Task<IServiceRemotingResponseMessage> HandleRequestResponseAsync(IServiceRemotingRequestContext requestContext, IServiceRemotingRequestMessage requestMessage)
    {

        return base.HandleRequestResponseAsync(requestContext, requestMessage);
    }
}

那么监听器就变成了

var fabricListener = new ServiceInstanceListener((context) =>
        {
        var fabricRemotingListener = new FabricTransportServiceRemotingListener(
        serviceContext: context,
        serviceRemotingMessageHandler: new TestRemotingDispatcher(context, this),
            remotingListenerSettings: new Microsoft.ServiceFabric.Services.Remoting.FabricTransport.Runtime.FabricTransportRemotingListenerSettings()
            {
                EndpointResourceName = "mngendpoint"
            },
            serializationProvider: null);


            return fabricRemotingListener;
        },
      "mnglistener");

这按预期工作。如果我弄清楚原因,我会更新。

编辑:我还没有弄清楚为什么显式分配 serviceRemotingMessageHandler 有效,但不需要上面显示的 ServiceRemotingMessageDispatcher 的子类型。它将通过分配 built-in 实现来工作:

 serviceRemotingMessageHandler: new ServiceRemotingMessageDispatcher(context, serviceInstance)

最终解释

这一切都归结为我的错误。请注意,我最初将 null 分配给 serviceRemotingMessageHandler,这是不正确的。查看源代码后,有一个构造函数将为您创建一个调度程序。通过按照我的方式分配 null,侦听器中不存在任何调度程序。

 public FabricTransportServiceRemotingListener(
        ServiceContext serviceContext,
        IService serviceImplementation,
        FabricTransportRemotingListenerSettings remotingListenerSettings = null,
        IServiceRemotingMessageSerializationProvider serializationProvider = null)
        : this(
              serviceContext,
              new ServiceRemotingMessageDispatcher(
                serviceContext,
                serviceImplementation,
                GetMessageBodyFactory(serializationProvider, remotingListenerSettings)),
              remotingListenerSettings,
              serializationProvider)
    {
    }

异常消息肯定让我走错了路,但至少对 (self-inflicted) 问题有一个很好的解释。

我刚遇到这个问题,对我来说这是接口的命名空间问题。愚蠢的错误我只是将 class 复制到另一个没有命名空间的项目中。