Comunicazione remota con i servizi con Reliable ServicesService Remoting with Reliable Services

Per i servizi che non sono legati a un protocollo di comunicazione o uno stack particolare, ad esempio WebAPI, Windows Communication Foundation (WCF) o altri, il framework Reliable Services fornisce un meccanismo remoto per impostare in modo semplice e rapido una chiamata di procedura remota per i servizi.For services that are not tied to a particular communication protocol or stack, such as WebAPI, Windows Communication Foundation (WCF), or others, the Reliable Services framework provides a remoting mechanism to quickly and easily set up remote procedure call for services.

Impostare la comunicazione remota in un servizioSet up Remoting on a Service

La procedura di impostazione della funzionalità remota per un servizio è costituita da due semplici passaggi.Setting up remoting for a service is done in two simple steps:

  1. Creare un'interfaccia per l’implementazione del servizio.Create an interface for your service to implement. Questa interfaccia definisce i metodi che sono disponibili per una chiamata di procedura remota nel servizioThis interface defines the methods that are available for a remote procedure call on your service. e devono essere metodi asincroni di restituzione di attività.The methods must be task-returning asynchronous methods. L'interfaccia deve implementare Microsoft.ServiceFabric.Services.Remoting.IService per segnalare che il servizio dispone di un'interfaccia remota.The interface must implement Microsoft.ServiceFabric.Services.Remoting.IService to signal that the service has a remoting interface.
  2. Usare un listener di comunicazione remota nel servizio.Use a remoting listener in your service. RemotingListener è un'implementazione ICommunicationListener che offre funzionalità di comunicazione remota.RemotingListener is an ICommunicationListener implementation that provides remoting capabilities. Lo spazio dei nomi Microsoft.ServiceFabric.Services.Remoting.Runtime contiene un metodo di estensione CreateServiceRemotingListener, per i servizi con e senza stato, che può essere usato per creare un listener di comunicazione remota con il protocollo di trasporto predefinito per la comunicazione remota.The Microsoft.ServiceFabric.Services.Remoting.Runtime namespace contains an extension method,CreateServiceRemotingListener for both stateless and stateful services that can be used to create a remoting listener using the default remoting transport protocol.

Nota

Lo spazio dei nomi Remoting è disponibile come pacchetto NuGet separato denominato Microsoft.ServiceFabric.Services.RemotingThe Remoting namespace is available as a separate NuGet package called Microsoft.ServiceFabric.Services.Remoting

Ad esempio, il servizio senza stato seguente espone un metodo singolo per ottenere "Hello World" su una chiamata RPC.For example, the following stateless service exposes a single method to get "Hello World" over a remote procedure call.

using Microsoft.ServiceFabric.Services.Communication.Runtime;
using Microsoft.ServiceFabric.Services.Remoting;
using Microsoft.ServiceFabric.Services.Remoting.Runtime;
using Microsoft.ServiceFabric.Services.Runtime;

public interface IMyService : IService
{
    Task<string> HelloWorldAsync();
}

class MyService : StatelessService, IMyService
{
    public MyService(StatelessServiceContext context)
        : base (context)
    {
    }

    public Task HelloWorldAsync()
    {
        return Task.FromResult("Hello!");
    }

    protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
    {
        return new[] { new ServiceInstanceListener(context =>            this.CreateServiceRemotingListener(context)) };
    }
}

Nota

Gli argomenti e i tipi restituiti nell'interfaccia del servizio possono essere semplici, complessi o personalizzati ma, in tutti i casi, devono essere serializzabili mediante il serializzatore .NET DataContractSerializer.The arguments and the return types in the service interface can be any simple, complex, or custom types, but they must be serializable by the .NET DataContractSerializer.

Chiamare i metodi del servizio remotoCall Remote Service Methods

La chiamata dei metodi su un servizio mediante lo stack remoto viene eseguita usando un proxy locale al servizio tramite la classe Microsoft.ServiceFabric.Services.Remoting.Client.ServiceProxy .Calling methods on a service by using the remoting stack is done by using a local proxy to the service through the Microsoft.ServiceFabric.Services.Remoting.Client.ServiceProxy class. Il metodo ServiceProxy crea un proxy locale usando la stessa interfaccia implementata dal servizio.The ServiceProxy method creates a local proxy by using the same interface that the service implements. Con tale proxy, è possibile chiamare i metodi nell'interfaccia in modalità remota.With that proxy, you can call methods on the interface remotely.


IMyService helloWorldClient = ServiceProxy.Create<IMyService>(new Uri("fabric:/MyApplication/MyHelloWorldService"));

string message = await helloWorldClient.HelloWorldAsync();

Il framework remoto propaga le eccezioni generate dal servizio al client.The remoting framework propagates exceptions thrown by the service to the client. Di conseguenza, quando si usa ServiceProxy, il client è responsabile per la gestione delle eccezioni generate dal servizio.As a result, when using ServiceProxy, the client is responsible for handling the exceptions thrown by the service.

Durata del proxy servizioService Proxy Lifetime

La creazione del proxy servizio è un'operazione semplice e, pertanto, l'utente può creare quanti proxy desidera.ServiceProxy creation is a lightweight operation, so users can create as many as they need. Le istanze del proxy servizio possono essere usate più volte, fintanto che l'utente ne ha necessità.Service Proxy instances can be reused as long as users need it. Se una chiamata di procedura remota genera un'eccezione, gli utenti possono comunque riusare la stessa istanza del proxy.If a remote procedure call throws an Exception, users can still reuse the same proxy instance. Ogni proxy servizio contiene un client di comunicazione usato per inviare messaggi sulla rete.Each ServiceProxy contains a communication client used to send messages over the wire. Durante chiamate remote, vengono effettuati controlli interni per verificare che il client di comunicazione sia valido.While invoking remote calls, we internally check to see if the communication client is valid. In base al risultato, il client di comunicazione viene ricreato.Based on that result, we re-create the communication client if needed. Pertanto, se si verifica un'eccezione, l'utente non deve ricreare ServiceProxy perché questa operazione viene eseguita in modo trasparente.Hence if an exception occurs, users do not need to recreate ServiceProxy because it is done so transparently.

Durata di ServiceProxyFactoryServiceProxyFactory Lifetime

ServiceProxyFactory è una factory che crea istanze di proxy per interfacce di connessione remota diverse.ServiceProxyFactory is a factory that creates proxy instances for different remoting interfaces. Se si usa l'API ServiceProxy.Create per la creazione di un proxy, il framework crea un singleton ServiceProxy.If you use the api ServiceProxy.Create for creating proxy, then the framework creates a singleton ServiceProxy. È utile per crearne una manualmente quando è necessario eseguire l'override delle proprietà IServiceRemotingClientFactory.It is useful to create one manually when you need to override IServiceRemotingClientFactory properties. La creazione di una factory è un'operazione costosa.Factory creation is an expensive operation. ServiceProxyFactory mantiene una cache interna del client di comunicazione.ServiceProxyFactory maintains an internal cache of communication client. La procedura consigliata consiste nel memorizzare nella cache ServiceProxyFactory il più a lungo possibile.Best practice is to cache ServiceProxyFactory for as long as possible.

Gestione delle eccezioni remoteRemoting Exception Handling

Tutte le eccezioni generate dall'API del servizio vengono inviate nuovamente al client come AggregateException.All remote exceptions thrown by the service API are sent back to the client as AggregateException. RemoteExceptions deve essere serializzabile per DataContract; in caso contrario, l'API del proxy genera una ServiceException contenente l'errore di serializzazione.RemoteExceptions should be DataContract serializable; if they are not, the proxy API throws ServiceException with the serialization error in it.

ServiceProxy gestisce tutte le eccezioni di failover per la partizione del servizio per la quale è stato creato.ServiceProxy handles all failover exceptions for the service partition it is created for. Risolve nuovamente gli endpoint in presenza di eccezioni di failover (eccezioni non temporanee) e tenta di nuovo la chiamata con l'endpoint corretto.It re-resolves the endpoints if there are failover exceptions (non-transient exceptions) and retries the call with the correct endpoint. Il numero di tentativi per le eccezioni di failover è indefinito.The number of retries for failover exceptions are indefinite. In caso di eccezioni temporanee, il proxy ripete la chiamata.If transient exceptions occur, the proxy retries the call.

I parametri di ripetizione dei tentativi predefiniti sono forniti da OperationRetrySettings.Default retry parameters are provied by OperationRetrySettings. L'utente può configurare questi valori passando l'oggetto OperationRetrySettings al costruttore ServiceProxyFactory.User can configure these values by passing OperationRetrySettings object to ServiceProxyFactory constructor.

Come usare lo stack V2 per la comunicazione remotaHow to use Remoting V2 stack

Con il pacchetto per la comunicazione remota NuGet 2.8, è possibile usare lo stack V2 per la comunicazione remota.With 2.8 NuGet Remoting package, you have the option to use Remoting V2 stack. Lo stack V2 per la comunicazione remota è più efficiente e offre funzionalità come API serializzabili personalizzate e più collegabili.Remoting V2 stack is more performant and provides features like custom serializable and more pluggable Api's. Per impostazione predefinita, se non si effettuano le modifiche seguenti, viene ancora usato lo stack V1 per la comunicazione remota.By default, if you don't do following changes, it continues to use Remoting V1 Stack. La comunicazione remota V2 non è compatibile con V1, ovvero con lo stack per la comunicazione remota precedente. Continuare a leggere l'articolo seguente su come passare da V1 a V2 senza compromettere la disponibilità del servizio.Remoting V2 is not compatible with V1(previous Remoting stack), so follow below article on how to upgrade from V1 to V2 without impacting service availability.

Uso dell'attributo assembly per usare lo stack V2.Using Assembly Attribute to use V2 stack.

Ecco i passaggi da seguire per passare allo stack V2.Here are the steps to follow to change to V2 Stack.

  1. Aggiungere una risorsa endpoint con il nome "ServiceEndpointV2" nel manifesto del servizio.Add an Endpoint Resource with name as "ServiceEndpointV2" in the service manifest.

    <Resources>
     <Endpoints>
       <Endpoint Name="ServiceEndpointV2" />  
     </Endpoints>
    </Resources>
    
  2. Usare il metodo di estensione per la comunicazione remota per creare un listener di comunicazione remota.Use Remoting Extension Method to create Remoting Listener.

    protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
    {
        return this.CreateServiceRemotingInstanceListeners();
    }
    
  3. Aggiungere l'attributo assembly nelle interfacce di comunicazione remota.Add Assembly Attribute on Remoting Interfaces.

    [assembly: FabricTransportServiceRemotingProvider(RemotingListener = RemotingListener.V2Listener, RemotingClient = RemotingClient.V2Client)]
    

    Non sono necessarie modifiche nel progetto client.No Changes are required in Client Project. Compilare l'assembly client con l'assembly dell'interfaccia, per assicurarsi che venga usato l'attributo assembly precedente.Build the Client assembly with interface assembly, to makes sure that above assembly attribute is being used.

Uso di classi V2 in modo esplicito per creare listener/factory clientUsing Explicit V2 Classes to create Listener/ ClientFactory

Ecco i passaggi da seguire.Here are the steps to follow.

  1. Aggiungere una risorsa endpoint con il nome "ServiceEndpointV2" nel manifesto del servizio.Add an Endpoint Resource with name as "ServiceEndpointV2" in the service manifest.

    <Resources>
    <Endpoints>
      <Endpoint Name="ServiceEndpointV2" />  
    </Endpoints>
    </Resources>
    
  2. Usare il listener V2 per la comunicazione remota.Use Remoting V2Listener. Il nome predefinito della risorsa per l'endpoint di servizio usato è "ServiceEndpointV2" e deve essere definito nel manifesto del servizio.Default Service Endpoint Resource name used is "ServiceEndpointV2" and must be defined in Service Manifest.

    protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
     {
         return new[]
         {
             new ServiceInstanceListener((c) =>
             {
                 return new FabricTransportServiceRemotingListener(c, this);
    
             })
         };
     }
    
  3. Usare factory client V2.Use V2 Client Factory.

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

Come eseguire l'aggiornamento dal servizio di comunicazione remota V1 a V2.How to upgrade from Remoting V1 to Remoting V2.

Per eseguire l'aggiornamento da V1 a V2, sono necessari gli aggiornamenti in due passaggi.In order to upgrade from V1 to V2, 2-step upgrades are required. La procedura seguente deve essere eseguita nell'ordine elencato.Following steps to be executed in the sequence listed.

  1. Aggiornare il servizio da V1 a V2 mediante l'attributo CompactListener.Upgrade the V1 Service to V2 Service by using CompactListener Attribute. Questa modifica assicura che il servizio sia in ascolto sul listener V1 e V2.This change makes sure that service is listening on V1 and V2 Listener.

    a) Aggiungere una risorsa endpoint con il nome "ServiceEndpointV2" nel manifesto del servizio.a) Add an Endpoint Resource with name as "ServiceEndpointV2" in the service manifest.

    <Resources>
      <Endpoints>
        <Endpoint Name="ServiceEndpointV2" />  
      </Endpoints>
    </Resources>
    

    b) Usare il metodo di estensione seguente per creare un listener di comunicazione remota.b) Use Following Extension Method to Create Remoting Listener.

    protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
    {
        return this.CreateServiceRemotingInstanceListeners();
    }
    

    c) Aggiungere l'attributo assembly nelle interfacce per la comunicazione remota per usare CompatListener e il client V2.c) Add Assembly Attribute on Remoting Interfaces to use CompatListener and V2 Client.

    [assembly: FabricTransportServiceRemotingProvider(RemotingListener = RemotingListener.CompatListener, RemotingClient = RemotingClient.V2Client)]
    
  2. Aggiornare il client V1 alla versione V2 con l'attributo client V2.Upgrade the V1 Client to V2 Client by using V2 Client Attribute. Questo passaggio si accerta che il client usi lo stack V2.This step makes sure Client is using V2 stack. Non è richiesta alcuna modifica nel progetto/servizio client.No Change in Client Project/Service is required. La compilazione di progetti client con assembly di interfaccia aggiornata è sufficiente.Building Client projects with updated interface assembly is sufficient.

  3. Questo passaggio è facoltativo.This step is optional. Usare l'attributo V2Listener e aggiornare il servizio V2.Use V2Listener Attribute and then Upgrade the V2 Service. Questo passaggio assicura che il servizio sia in ascolto solo sul listener V2.This step makes sure that service is listening only on V2 Listener.

[assembly: FabricTransportServiceRemotingProvider(RemotingListener = RemotingListener.V2Listener, RemotingClient = RemotingClient.V2Client)]

Come usare la serializzazione personalizzata con la comunicazione remota V2.How to use Custom Serialization with Remoting V2.

L'esempio seguente usa la serializzazione Json con la comunicazione remota V2.Following example uses Json Serialization with Remoting V2.

  1. Implementare l'interfaccia IServiceRemotingMessageSerializationProvider per offrire l'implementazione per la serializzazione personalizzata.Implement IServiceRemotingMessageSerializationProvider interface to provide implementation for custom serialization. Di seguito è riportato il frammento di codice che illustra come dovrebbe apparire l'implementazione.Here is the code-snippet on how implementation looks like.

    public class ServiceRemotingJsonSerializationProvider : IServiceRemotingMessageSerializationProvider
    {
      public IServiceRemotingRequestMessageBodySerializer CreateRequestMessageSerializer(Type serviceInterfaceType,
          IEnumerable<Type> requestBodyTypes)
      {
          return new ServiceRemotingRequestJsonMessageBodySerializer(serviceInterfaceType, requestBodyTypes);
      }
    
      public IServiceRemotingResponseMessageBodySerializer CreateResponseMessageSerializer(Type serviceInterfaceType,
          IEnumerable<Type> responseBodyTypes)
      {
          return new ServiceRemotingResponseJsonMessageBodySerializer(serviceInterfaceType, responseBodyTypes);
      }
    
      public IServiceRemotingMessageBodyFactory CreateMessageBodyFactory()
      {
          return new JsonMessageFactory();
      }
     }
    
    class JsonMessageFactory: IServiceRemotingMessageBodyFactory
    {
      public IServiceRemotingRequestMessageBody CreateRequest(string interfaceName, string methodName,
          int numberOfParameters)
      {
          return new JsonRemotingRequestBody(new JObject());
      }
    
      public IServiceRemotingResponseMessageBody CreateResponse(string interfaceName, string methodName)
      {
          return  new JsonRemotingResponseBody();
      }
    }
    
    class ServiceRemotingRequestJsonMessageBodySerializer: IServiceRemotingRequestMessageBodySerializer
    {
      public ServiceRemotingRequestJsonMessageBodySerializer(Type serviceInterfaceType,
          IEnumerable<Type> parameterInfo)
      {
      }
    
      public OutgoingMessageBody Serialize(IServiceRemotingRequestMessageBody serviceRemotingRequestMessageBody)
      {
          if (serviceRemotingRequestMessageBody == null)
          {
              return null;
          }
    
          var json = serviceRemotingRequestMessageBody.ToString();
          var bytes = Encoding.UTF8.GetBytes(json);
          var segment = new ArraySegment<byte>(bytes);
          var segments = new List<ArraySegment<byte>> {segment};
          return new OutgoingMessageBody(segments);
      }
    
      public IServiceRemotingRequestMessageBody Deserialize(IncomingMessageBody messageBody)
      {
          using (var sr = new StreamReader(messageBody.GetReceivedBuffer()))
    
          using (JsonReader reader = new JsonTextReader(sr))
          {
              var serializer = new JsonSerializer();
              var ob = serializer.Deserialize<JObject>(reader);
              var ob2 = new JsonRemotingRequestBody(ob);
              return ob2;
          }
      }
    }
    
    class ServiceRemotingResponseJsonMessageBodySerializer: IServiceRemotingResponseMessageBodySerializer
    {
    
      public ServiceRemotingResponseJsonMessageBodySerializer(Type serviceInterfaceType,
          IEnumerable<Type> parameterInfo)
      {
      }
    
      public OutgoingMessageBody Serialize(IServiceRemotingResponseMessageBody responseMessageBody)
      {
          var json = JsonConvert.SerializeObject(responseMessageBody,new JsonSerializerSettings()
          {
              TypeNameHandling = TypeNameHandling.All
          });
          var bytes = Encoding.UTF8.GetBytes(json);
          var segment = new ArraySegment<byte>(bytes);
          var list = new List<ArraySegment<byte>> {segment};
          return new OutgoingMessageBody(list);
      }
    
      public IServiceRemotingResponseMessageBody Deserialize(IncomingMessageBody messageBody)
      {
          using (var sr = new StreamReader(messageBody.GetReceivedBuffer()))
    
          using (var reader = new JsonTextReader(sr))
          {
              var serializer = JsonSerializer.Create(new JsonSerializerSettings()
              {
                  TypeNameHandling = TypeNameHandling.All
              });
              return serializer.Deserialize<JsonRemotingResponseBody>(reader);
          }
      }
    }
    internal class JsonRemotingResponseBody: IServiceRemotingResponseMessageBody
    {
      public object Value;
    
      public void Set(object response)
      {
          this.Value = response;
      }
    
      public object Get(Type paramType)
      {
          return this.Value;
      }
    }
    
    class JsonRemotingRequestBody: IServiceRemotingRequestMessageBody
    {
      private readonly JObject jobject;
    
        public JsonRemotingRequestBody(JObject ob)
        {
            this.jobject = ob;
        }
    
        public void SetParameter(int position, string parameName, object parameter)
        {
            this.jobject.Add(parameName, JToken.FromObject(parameter));
        }
    
        public object GetParameter(int position, string parameName, Type paramType)
       {
           var ob = this.jobject[parameName];
           return ob.ToObject(paramType);
       }
    
       public override string ToString()
        {
            return this.jobject.ToString();
        }
    }
    
  2. Eseguire l'override del provider di serializzazione predefinito con JsonSerializationProvider per il listener di comunicazione remota.Override Default Serialization Provider with JsonSerializationProvider for Remoting Listener.

    protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
    {
     return new[]
     {
         new ServiceInstanceListener((c) =>
         {
             return new FabricTransportServiceRemotingListener(c, this,
                 new ServiceRemotingJsonSerializationProvider());
         })
     };
    }
    
  3. Eseguire l'override del provider di serializzazione predefinito con JsonSerializationProvider per il factory client di comunicazione remota.Override Default Serialization Provider with JsonSerializationProvider for Remoting Client Factory.

  var proxyFactory = new ServiceProxyFactory((c) =>
            {
                return new FabricTransportServiceRemotingClientFactory(
                    serializationProvider: new ServiceRemotingJsonSerializationProvider());
            });

Passaggi successiviNext steps