【问题标题】:How to call a service fabric service from a Winform application?如何从 Winform 应用程序调用服务结构服务?
【发布时间】:2019-03-22 10:52:59
【问题描述】:

我们有两个 Service Fabric 无状态服务。我们已将 Service Fabric 运行时升级到最新版本,现在想使用最新的 V2(V2_1) 运行时进行通信。我们还将 Service Fabric SDK 升级到了最新版本。

  1. 数学服务。它有两个暴露于通信的端点。
  2. EvilMathTeacherService。它调用这两个端点来做一些事情。它还暴露了一个端点。

这两个服务可以使用最新的 V2(V2_1) 运行时相互通信。 我们有另一个应用程序,一个 WinForm 应用程序,它想要调用 EvilMathTeacherService 来让它做一些坏事。问题是当我们使用 V2(V2_1) 运行时从位于结构集群外部的这个 Winform 应用程序调用服务结构服务时,它不起作用。它不做任何事情。

所以我的问题是——我能做些什么来实现它吗?或者在最新的 V2(V2_1) 运行时中是否无法从外部与服务进行通信?我们可以使用 V1 运行时来做到这一点,而且在旧的 V1 运行时中它完全没问题。

代码示例:(相关部分)

服务 1:MathService.cs

protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
{
  return new[]
 {
     new ServiceInstanceListener(
      context => new FabricTransportServiceRemotingListener(
        context,
        _mathCalculator_v2,
        new FabricTransportRemotingListenerSettings()
        {
            EndpointResourceName = "MathCalculator_v2"
        }),
      "MathCalculator_v2"),
     new ServiceInstanceListener(
      context => new FabricTransportServiceRemotingListener(
        context,
        _textManipulator_v2,
        new FabricTransportRemotingListenerSettings()
        {
            EndpointResourceName = "TextManipulator_v2"
        }),
      "TextManipulator_v2")
 };
}

服务清单:

<Resources>
<Endpoints>
  <!-- This endpoint is used by the communication listener to obtain the port on which to 
       listen. Please note that if your service is partitioned, this port is shared with 
       replicas of different partitions that are placed in your code. -->
  <Endpoint Name="ServiceEndpointV2_1" />
</Endpoints>

服务 2:EvilMathTeacherService.cs

protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
{
  return new[]
 {
       new ServiceInstanceListener(
        context => new FabricTransportServiceRemotingListener(
          context,
          _questionnaire,
          new FabricTransportRemotingListenerSettings()
          {
              EndpointResourceName = "Questionnaire_v2"

          }),
        "Questionnaire_v2")
   };
}

在 RunAsync 方法中:

protected override async Task RunAsync(CancellationToken cancellationToken)
{
  // TODO: Replace the following sample code with your own logic 
  //       or remove this RunAsync override if it's not needed in your service.

  long iterations = 0;
  while (true)
  {
    cancellationToken.ThrowIfCancellationRequested();
    ServiceEventSource.Current.ServiceMessage(this.Context, "Evil teacher is coming to get you!-{0}", ++iterations);

    Random r = new Random();
    int first = r.Next(0, 100);
    var second = r.Next(200, 400);


    try
    {
      var sum = await _questionnaire.AddTwoNumbers(first, second);

      ServiceEventSource.Current.ServiceMessage(this.Context, "Evil Math teacher says - Sum-{0}", sum);

      var ifEqual = await _questionnaire.CheckIfTwoNumbersAreEqual(first, second);
      ServiceEventSource.Current.ServiceMessage(this.Context, "Evil Math teacher says - If Equal-{0}", ifEqual);

    }
    catch (Exception ex)
    {

      throw;
    }
    await Task.Delay(TimeSpan.FromSeconds(2), cancellationToken);
  }
}

这是调用 MathService 的 Questionnaire.cs 类

public async Task<int> AddTwoNumbers(int a, int b)
{
  var uri = new Uri("fabric:/SampleSFV2/MathService");
  var proxyFactory = new ServiceProxyFactory((c) =>
  {
    var settings = new FabricTransportRemotingSettings();
    return new FabricTransportServiceRemotingClientFactory(settings);
  });

  var service = proxyFactory.CreateServiceProxy<IMathCalculator>(uri, listenerName: "MathCalculator_v2");

  return await service.Add(a, b);
}

public async Task<bool> CheckIfTwoNumbersAreEqual(int a, int b)
{
  var text1 = a.ToString();
  var text2 = b.ToString();

  var uri = new Uri("fabric:/SampleSFV2/MathService");
  var proxyFactory = new ServiceProxyFactory((c) =>
  {
    var settings = new FabricTransportRemotingSettings();
    return new FabricTransportServiceRemotingClientFactory(settings);
  });

  var service = proxyFactory.CreateServiceProxy<ITextManipulator>(uri, listenerName: "TextManipulator_v2");

  return await service.IfEqual(text1, text2);
}

这按预期工作。但是,当我从我的 winform 应用程序对 EvilMathTeacherService 或 MathService 本身进行类似调用时,该调用似乎无法到达服务。

Winform 应用程序部分: Form1.cs

private async void button_AddNumbers_Click(object sender, EventArgs e)
{
  //int number1 = int.Parse(textBox_Number1.Text);
  //int number2 = int.Parse(textBox_Number2.Text);

  //var uri = _evilMathServiceUri;

  int number1 = 10;
  int number2 = 100;

  var uri = new Uri("fabric:/SampleSFV2/EvilMathTeacherService");

  ServiceProxyFactory proxyFactory = new ServiceProxyFactory((c) =>
  {
    FabricTransportRemotingSettings settings = new FabricTransportRemotingSettings();
    return new FabricTransportServiceRemotingClientFactory(settings);
  });

  try
  {
    IQuestionnaire service = proxyFactory.CreateServiceProxy<IQuestionnaire>(uri, listenerName: "Questionnaire_v2");
    int result = await service.AddTwoNumbers(number1, number2);

    MessageBox.Show("Result= " + result);
  }
  catch (Exception ex)
  {
    MessageBox.Show(ex.ToString());
  }
}

我正在关注他们官方文档中的此链接- https://docs.microsoft.com/en-us/azure/service-fabric/service-fabric-reliable-services-communication-remoting#how-to-use-remoting-v2-stack

请指出我在这里遗漏的任何内容。再次断言 - 我在使用 Microsoft.ServiceFabric.Services.Remoting.V2.FabricTransport.Runtime 从结构集群外部的 Winform 应用程序调用服务时遇到问题。

或者这不能再使用 V2 运行时完成?因为我们正在使用 V1 运行时成功地做同样的事情。

示例代码项目:

https://github.com/nirjash13/azure-service-fabric

【问题讨论】:

  • 只是为了确定,您是否将客户端更改为使用 V2 命名空间中的 FabricTransportServiceRemotingClientFactorydocs.microsoft.com/en-us/dotnet/api/…
  • 您的示例项目混合了 v1 和 v2。您是否按照此处的指导进行操作? docs.microsoft.com/en-us/azure/service-fabric/…
  • @LoekD:是的,我做到了。那是它不再起作用的时候。如果我使用 V1 命名空间中的远程客户端,它就像一个魅力。
  • @LoekD:这可能是因为我想演示 V1 可以工作但 V2 不行。目前,我没有使用 V1 命名空间的引用。我确实遵循了他们的文档。
  • 您混淆了 V1、V2 和 V2_1。如果要使用 V2,请将端点指定为 V2,或使用 V2_1 并指定 settings.UseWrappedMessage = true;

标签: c# azure azure-service-fabric service-fabric-stateless


【解决方案1】:

首先澄清一点困惑,没有“V1 Service Fabric 运行时”或“V2 Service Fabric 运行时”。您看到的是 Service Remoting,它是 Service Fabric 的 .NET RPC 实现(客户端上的 Microsoft.ServiceFabric.Services.Remoting 命名空间和 ServiceProxyFactory)。

默认情况下,服务远程处理仅适用于集群内的服务之间,因为它使用临时范围内随机分配的端口,假设客户端-服务器之间存在直接连接。如果您有一个像负载均衡器这样的 NAT 设备,需要明确打开端口,这将不起作用。

服务远程处理在 Service Fabric 中是完全可选的,您不必使用它。对于客户端通信,我强烈建议您不要使用它,因为它会将您的客户端应用程序与您的后端服务紧密耦合(甚至包含共享程序集),并且稍后对您的 API 进行版本控制将是一场噩梦.我建议在您的 SF 服务中使用 ASP.NET Core 来将 HTTP API 公开给您的 WebForms 应用程序。

【讨论】:

  • 关于服务远程处理 - 您能否提供一个适合使用它的示例?只是感兴趣。
  • Service remoting by default only works between services within a cluster because it uses randomly assigned ports from the ephemeral range 这只有在您使用随机端口时才会出现问题(推荐的方法),就像任何其他服务一样,如果您使用随机端口,您还必须在 LB 中打开端口或使用网关\代理在集群内转发它。
猜你喜欢
  • 2011-04-04
  • 1970-01-01
  • 2021-10-13
  • 2018-03-08
  • 1970-01-01
  • 2020-02-19
  • 2018-12-04
  • 2017-08-15
  • 2017-11-25
相关资源
最近更新 更多