1
votes

We have two Service Fabric stateless services. We have upgraded our service fabric runtime to the latest version and now want to use the latest V2(V2_1) runtime for communication. We have also upgraded the Service Fabric SDK to the latest version.

  1. MathService. It has two endpoints exposed to communication.
  2. EvilMathTeacherService. It calls those two endpoints to do some things. It also has an endpoint exposed.

These two services can talk to each other using the latest V2(V2_1) runtime. We have another application, a WinForm application, that wants to call the EvilMathTeacherService to make it do something evil. Problem is when we use the V2(V2_1) runtime to make the call to the service fabric service/s from this Winform application which resides outside the fabric cluster, it does not work. It does not do anything.

So my question would be- is there anything I can do to make it happen? Or is there no way to communicate to the services from outside in the latest V2(V2_1) runtime? We are able to do this using the V1 runtime and it's perfectly fine in the old V1 runtime.

Code sample: (Relevant portion)

Service 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")
 };
}

ServiceManifest:

<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>

Service 2: EvilMathTeacherService.cs

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

          }),
        "Questionnaire_v2")
   };
}

In the RunAsync Method:

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

This is the Questionnaire.cs class that's making the call to the MathService

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

This is working as expected. But when I make a similar call from my winform application to the EvilMathTeacherService or the MathService itself, the call doesn't seem to make it to the services.

Winform application portion: 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());
  }
}

I am following this link from their official documentation- https://docs.microsoft.com/en-us/azure/service-fabric/service-fabric-reliable-services-communication-remoting#how-to-use-remoting-v2-stack

Please point out anything I am missing here. To assert again - I am having the problem when calling the services from a Winform application outside the fabric cluster using the Microsoft.ServiceFabric.Services.Remoting.V2.FabricTransport.Runtime.

Or this cannot be done using the V2 runtime anymore? Because we are doing the same thing successfully using the V1 runtime.

Sample code project:

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

1
just to be sure, did you change the client to use the FabricTransportServiceRemotingClientFactory from the V2 namespace? docs.microsoft.com/en-us/dotnet/api/…LoekD
Your sample project mixes up v1 and v2. Did you follow the guidance from here? docs.microsoft.com/en-us/azure/service-fabric/…LoekD
@LoekD: Yes I did. That's when it does not work anymore. If I use the remoting client from the V1 namespace, it works like a charm.Safkat
@LoekD: That's probably because I wanted to demo that V1 works but V2 does not. And currently, I do not have references that are using the V1 namespace. And I did follow their documentation.Safkat
you're mixing up V1, V2 and V2_1. If you want to use V2, specify the endpoints as V2, or use V2_1 and specify settings.UseWrappedMessage = true;LoekD

1 Answers

0
votes

Just to clear up a little confusion first, there is no "V1 Service Fabric runtime" or "V2 Service Fabric runtime". What you're seeing refers to Service Remoting, which is a .NET RPC implementation for Service Fabric (Microsoft.ServiceFabric.Services.Remoting namespace and ServiceProxyFactory on the client).

Service remoting by default only works between services within a cluster because it uses randomly assigned ports from the ephemeral range, which assumes direct connectivity between client-server. If you have a NAT device like a load balancer in between that requires explicitly opening ports, this won't work.

Service remoting is completely optional in Service Fabric and you don't have to use it. For client communication, I strongly recommend you don't use it, as it will tightly couple your client application to your back-end services (with shared assemblies even) and versioning your APIs later will be a nightmare. I recommend using ASP.NET Core in your SF services to expose an HTTP API to your WebForms application instead.