1

我正在开发一个简单的 Service Fabric 群集,我想从无状态 ASP.NET Core 2.0 Web API 调用无状态服务。

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

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:在 Microsoft.ServiceFabric.Services.Remoting.V2 上找不到具有此 ID -488762776 的接口。 ServiceRemotingMessageSerializersManager.GetInterfaceDetails(Int32 interfaceId) 在 Microsoft.ServiceFabric.Services.Remoting.V2.ServiceRemotingMessageSerializersManager.CreateSerializers(Int32 interfaceId) 在 System.Collections.Concurrent.ConcurrentDictionary2.GetOrAdd(TKey key, Func2 valueFactory) 在 Microsoft.ServiceFabric.Services.Remoting.V2.FabricTransport.Runtime.FabricTransportMessageHandler.CreateRemotingRequestMessage(FabricTransportMessage fabricTransportMessage, Stopwatch stopwatch) 在 Microsoft.ServiceFabric.Services.GetRequestBodySerializer(Int32 interfaceId) 在 Microsoft.ServiceFabric .Services.Remoting.V2.FabricTransport.Runtime.FabricTransportMessageHandler.d__7.MoveNext())'

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

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

4

3 回答 3

1

请参阅此答案的底部以直接获得最终解决方案。

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

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

 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 的子类型不是必需的。它将通过分配内置实现来工作:

 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)
    {
    }

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

于 2018-08-08T17:47:17.507 回答
0

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

public async Task<MyDto> Get()
{

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

    return await svc.GetMahDto(23);
}

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

于 2018-08-08T06:11:21.637 回答
0

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

于 2018-08-09T10:58:44.673 回答