服务结构:System.ArgumentException:找不到Id为488762776的接口



我正在一个简单的Service Fabric集群上工作,我想在那里从无状态ASP.NET Core 2.0 web API调用无状态服务。

我做的第一件事是创建一个.NET Standard 2.0类库,它有一个简单的接口和DTO:

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:找不到此Id的接口-488762776位于Microsoft.ServiceFabric.Services.Remoting.V2.ServiceRemotingMessageSerializersManager.GetInterfaceDetails(Int32接口ID(位于Microsoft.ServiceFabric.Services.Remoting.V2.ServiceRemotingMessageSerializersManager.CreateSerializers(Int32接口ID(位于System.Collections.Concurrent.CurrentDictionary2.GetOrAdd(TKey key, Func2 valueFactory(位于Microsoft.ServiceFabric.Services.Remoting.V2.ServiceRemotingMessageSerializersManager.GetRequestBodySerizer(Int32接口ID(位于Microsoft.ServiceFabric.Services.Remoting.V2.FabricTransport.Runtime.FabricTransportMessageHandler.CreateRemotingRequestMessage(FabricTransportMessage FabricTransportMessage,秒表(位于Microsoft.ServiceFabric.Services.Remoting.V2.FabricTransport.Runtime.FabricTransportMessageHandler.d_7.MoveNext((('

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

有什么想法我可能会在哪里出错吗?

请参阅此答案的底部,直接进入最终解决方案。

我找到了一个部分令人满意的答案。然而,我不完全知道为什么它有效。我偶然发现这件事纯属偶然。我一直在研究一种方法,可以无缝地通过结构请求传递标头,这就是我最终实现这一点的原因。

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

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有效,但上面显示的ServiceRemotingMessage Dispatcher的子类型不是必需的。它将通过分配内置实现来工作:

serviceRemotingMessageHandler: new ServiceRemotingMessageDispatcher(context, serviceInstance)

最终解释

这一切都归结为我的错误。注意,我一开始给serviceRemotingMessageHandler分配了null,这是不正确的。在查看了源代码之后,有一个构造函数将为您创建一个调度器。通过以我的方式分配null,监听器中不存在调度器。

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

异常消息无疑让我走上了错误的道路,但至少对(自己造成的(问题有一个很好的解释。

我确实遇到过一次同样的问题,除了使用NonIServiceProxy方法之外,我无法解决它:

public async Task<MyDto> Get()
{
var svc = ServiceProxy.CreateNonIServiceProxy<ContractsStandard.IMicroService>(new Uri("fabric:/FabricSandbox4/TheService"), listenerName: "mylistener");
return await svc.GetMahDto(23);
}

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

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

最新更新