Sdílet prostřednictvím


Vzdálená komunikace ve službě C# s funkcemi Reliable Services

Pro služby, které nejsou svázány s konkrétním komunikačním protokolem nebo zásobníkem, jako je webové rozhraní API, Windows Communication Foundation nebo jiné, poskytuje architektura Reliable Services mechanismus vzdáleného volání, který umožňuje rychlé a snadné nastavení vzdálených procedurálních volání pro služby. Tento článek popisuje, jak nastavit vzdálená volání procedur pro služby napsané pomocí jazyka C#.

Nastavení vzdálené komunikace ve službě

Nastavení vzdáleného ovládání pro službu můžete provést ve dvou jednoduchých krocích:

  1. Vytvořte rozhraní pro implementaci vaší služby. Toto rozhraní definuje metody, které jsou k dispozici pro vzdálené volání procedury ve vaší službě. Metody musí být asynchronní metody vracející úkoly. Rozhraní musí implementovat Microsoft.ServiceFabric.Services.Remoting.IService, aby signalizovalo, že služba má rozhraní pro vzdálenou komunikaci.
  2. Ve službě použijte vzdálený nasluchač. Naslouchací služba je ICommunicationListener implementace, která poskytuje funkce pro vzdálenou komunikaci. Microsoft.ServiceFabric.Services.Remoting.Runtime Obor názvů obsahuje rozšiřující metodu CreateServiceRemotingInstanceListeners pro bezstavové i stavové služby, kterou lze použít k vytvoření posluchače pomocí výchozího komunikačního protokolu vzdálené komunikace.

Poznámka:

Obor názvů Remoting je k dispozici jako samostatný NuGet balíček zvaný Microsoft.ServiceFabric.Services.Remoting.

Například následující bezstavová služba poskytuje jednu metodu k získání "Hello World" přes vzdálené volání procedury.

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<string> HelloWorldAsync()
    {
        return Task.FromResult("Hello!");
    }

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

Poznámka:

Argumenty a návratové typy v rozhraní služby mohou být jakékoli jednoduché, složité nebo vlastní typy, ale musí být možné serializovat rozhraním .NET DataContractSerializer.

Volání metod vzdálené služby

Poznámka:

Pokud používáte více než jeden oddíl, musí být serviceProxy.Create() poskytnut příslušný ServicePartitionKey. To není potřeba pro scénář jediného diskového oddílu.

Volání metod ve službě pomocí zásobníku vzdálené komunikace se provádí prostřednictvím Microsoft.ServiceFabric.Services.Remoting.Client.ServiceProxy třídy pomocí místní proxy ke službě. Metoda ServiceProxy vytvoří místní proxy pomocí stejného rozhraní, které služba implementuje. S tímto proxy serverem můžete vzdáleně volat metody v rozhraní.


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

string message = await helloWorldClient.HelloWorldAsync();

Architektura vzdálené komunikace přenáší výjimky vyvolané službou na klienta. V důsledku toho je klient zodpovědný za zpracování výjimek vyvolaných službou, když je použito ServiceProxy.

Životnost proxy serveru služby

Vytvoření proxy serveru služby je jednoduchá operace, takže můžete vytvořit tolik, kolik potřebujete. Instance proxy služby je možné opakovaně používat, pokud jsou potřeba. Pokud vzdálené volání procedury vyvolá výjimku, můžete stále znovu použít stejnou instanci proxy serveru. Každý proxy server služby obsahuje komunikačního klienta sloužícího k odesílání zpráv přes drát. Při volání vzdálených volání se provádí interní kontroly, které určují, jestli je komunikační klient platný. Na základě výsledků těchto kontrol se komunikační klient v případě potřeby znovu vytvoří. Proto pokud dojde k výjimce, není nutné znovu vytvořit ServiceProxy.

Životnost proxy služby

ServiceProxyFactory je továrna, která vytváří instance proxy pro různá rozhraní vzdálené komunikace. Pokud k vytvoření proxy serveru použijete rozhraní API ServiceProxyFactory.CreateServiceProxy , vytvoří rozhraní proxy jednoúčelové služby. Je užitečné ho vytvořit ručně, když potřebujete přepsat vlastnosti IServiceRemotingClientFactory. Vytváření továrny je náročná operace. Služba proxy factory udržuje interní mezipaměť komunikačního klienta. Osvědčeným postupem je ukládat do mezipaměti objekt pro vytváření proxy služeb co nejdéle.

Zpracování výjimek v rámci vzdáleného přístupu

Všechny vzdálené výjimky vyvolané rozhraním API služby se odešlou zpět klientovi jako AggregateException. Vzdálené výjimky by měly být serializovatelné pomocí DataContract. Pokud nejsou splněny tyto podmínky, rozhraní API proxy generuje výjimku ServiceException s chybou serializace.

Proxy služby zpracovává všechny výjimky při selhání pro oddíl služby, pro který byl vytvořen. Znovu vyřeší koncové body, pokud dojde k výjimkám převzetí služeb při selhání (ne přechodné výjimky) a opakuje volání se správným koncovým bodem. Počet opakování výjimek při selhání je neomezený. Pokud dojde k přechodným výjimkám, proxy volání opakuje.

Výchozí parametry opakování poskytuje OperationRetrySettings.

Uživatel může tyto hodnoty nakonfigurovat předáním OperationRetrySettings objektu ServiceProxyFactory konstruktoru.

Použití zásobníku vzdálené komunikace V2

Od verze 2.8 balíčku NuGet pro vzdálené volání máte možnost použít zásobník V2 pro vzdálené volání. Vrstva vzdálené komunikace V2 je výkonnější. Poskytuje také funkce, jako je vlastní serializace a další připojitelná rozhraní API. Kód šablony nadále používá zásobník pro vzdálenou komunikaci V1. Vzdálená komunikace V2 není kompatibilní s V1, což byl předcházející zásobník pro vzdálenou komunikaci. Postupujte podle pokynů v článku Upgrade z V1 na V2 , abyste se vyhnuli dopadům na dostupnost služeb.

Pro aktivaci zásobníku V2 jsou k dispozici následující metody.

Použijte atribut sestavení k použití zásobníku V2

Tyto kroky změní kód šablony, aby používal stack V2 pomocí sestavovacího atributu.

  1. Změňte v manifestu služby prostředek koncového bodu z "ServiceEndpoint" na "ServiceEndpointV2".

    <Resources>
     <Endpoints>
       <Endpoint Name="ServiceEndpointV2" />
     </Endpoints>
    </Resources>
    
  2. Pomocí metody rozšíření vytvořte moduly pro vzdálené naslouchání (pro V1 i V2 totožné).

     protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
     {
         return this.CreateServiceRemotingInstanceListeners();
     }
    
  3. Označte sestavení, které obsahuje rozhraní vzdáleného přístupu, s atributem FabricTransportServiceRemotingProvider.

    [assembly: FabricTransportServiceRemotingProvider(RemotingListenerVersion = RemotingListenerVersion.V2, RemotingClientVersion = RemotingClientVersion.V2)]
    

V klientském projektu nejsou vyžadovány žádné změny kódu. Sestavte klientské sestavení pomocí sestavení rozhraní, abyste se ujistili, že se používá dříve zobrazený atribut sestavení.

Použití explicitních tříd V2 k použití zásobníku V2

Jako alternativu k použití atributu sestavení lze zásobník V2 povolit také pomocí explicitních tříd V2.

Tyto kroky změní kód šablony tak, aby používal zásobník V2 pomocí explicitních tříd V2.

  1. Změňte koncový bod v manifestu služby z "ServiceEndpoint" na "ServiceEndpointV2".

    <Resources>
     <Endpoints>
       <Endpoint Name="ServiceEndpointV2" />
     </Endpoints>
    </Resources>
    
  2. Použijte FabricTransportServiceRemotingListener z Microsoft.ServiceFabric.Services.Remoting.V2.FabricTransport.Runtime oboru názvů.

    protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
     {
         return new[]
         {
             new ServiceInstanceListener((c) =>
             {
                 return new FabricTransportServiceRemotingListener(c, this);
    
             })
         };
     }
    
  3. K vytvoření klientů použijte FabricTransportServiceRemotingClientFactory z Microsoft.ServiceFabric.Services.Remoting.V2.FabricTransport.Client oboru názvů.

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

Upgrade z verze vzdáleného ovládání V1 na verzi vzdáleného ovládání V2

K upgradu z V1 na V2 se vyžadují dvoustupňové upgrady. Postupujte podle kroků v této sekvenci.

  1. Pomocí tohoto atributu upgradujte službu V1 na službu V2. Tato změna zajišťuje, aby služba poslouchala na posluchači V1 a V2.

    a. Přidejte prostředek koncového bodu s názvem ServiceEndpointV2 v manifestu služby.

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

    b) K vytvoření naslouchacího procesu vzdálené komunikace použijte následující metodu rozšíření.

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

    c. Přidejte atribut kompilace pro vzdálená rozhraní k použití naslouchací služby V1 a V2 a klienta V2.

    [assembly: FabricTransportServiceRemotingProvider(RemotingListenerVersion = RemotingListenerVersion.V2|RemotingListenerVersion.V1, RemotingClientVersion = RemotingClientVersion.V2)]
    
    
  2. Upgradujte klienta V1 na klienta V2 pomocí atributu klienta V2. Tento krok zajistí, že klient používá zásobník V2. V klientském projektu nebo službě není nutná žádná změna. Vytváření klientských projektů s aktualizovaným sestavením rozhraní stačí.

  3. Tento krok je volitelný. Použijte atribut naslouchátka V2 a poté upgradujte službu V2. Tento krok zajistí, že služba naslouchá pouze na posluchači V2.

    [assembly: FabricTransportServiceRemotingProvider(RemotingListenerVersion = RemotingListenerVersion.V2, RemotingClientVersion = RemotingClientVersion.V2)]
    

Použijte stack vzdálené komunikace V2 (kompatibilní s rozhraním)

Zásobník vzdálené komunikace V2 (kompatibilní s rozhraním) se označuje jako V2_1 a je nejvíce up-to-date verze. Má všechny funkce systému vzdálené komunikace V2. Jeho zásobník rozhraní je kompatibilní se zásobníkem vzdálené komunikace V1, ale není zpětně kompatibilní s V2 a V1. Pokud chcete upgradovat z verze 1 na V2_1, aniž by to mělo vliv na dostupnost služby, postupujte podle kroků v článku Upgrade z verze 1 na V2 (kompatibilní s rozhraním).

Použijte atribut sestavení k použití zásobníku V2 pro vzdálenou komunikaci (kompatibilní s rozhraním)

Podle těchto kroků přejděte na stack V2_1.

  1. Přidejte prostředek koncového bodu s názvem "ServiceEndpointV2_1" v manifestu služby.

    <Resources>
     <Endpoints>
       <Endpoint Name="ServiceEndpointV2_1" />  
     </Endpoints>
    </Resources>
    
  2. K vytvoření naslouchacího procesu vzdálené komunikace použijte metodu rozšíření vzdálené komunikace.

     protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
     {
         return this.CreateServiceRemotingInstanceListeners();
     }
    
  3. Přidejte atribut sestavení pro rozhraní vzdálené komunikace.

     [assembly:  FabricTransportServiceRemotingProvider(RemotingListenerVersion=  RemotingListenerVersion.V2_1, RemotingClientVersion= RemotingClientVersion.V2_1)]
    
    

V klientském projektu nejsou vyžadovány žádné změny. Sestavte klientské sestavení pomocí sestavení rozhraní, abyste měli jistotu, že se používá předchozí atribut sestavení.

Použij explicitní třídy vzdálené komunikace k vytvoření naslouchací komponenty nebo klientské továrny pro V2 verzi (kompatibilní s rozhraním)

Postupujte takto:

  1. Přidejte prostředek koncového bodu s názvem "ServiceEndpointV2_1" v manifestu služby.

    <Resources>
     <Endpoints>
       <Endpoint Name="ServiceEndpointV2_1" />  
     </Endpoints>
    </Resources>
    
  2. Použijte naslouchací proces vzdálené komunikace V2. Výchozí název prostředku koncového bodu služby, který se používá, je "ServiceEndpointV2_1". Musí být definován v manifestu služby.

    protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
     {
         return new[]
         {
             new ServiceInstanceListener((c) =>
             {
                 var settings = new FabricTransportRemotingListenerSettings();
                 settings.UseWrappedMessage = true;
                 return new FabricTransportServiceRemotingListener(c, this,settings);
    
             })
         };
     }
    
  3. Použijte klientskou továrnu V2.

    var proxyFactory = new ServiceProxyFactory((c) =>
           {
             var settings = new FabricTransportRemotingSettings();
             settings.UseWrappedMessage = true;
             return new FabricTransportServiceRemotingClientFactory(settings);
           });
    

Upgrade vzdálené komunikace z V1 na V2 (kompatibilní s rozhraním)

K upgradu z V1 na V2 (kompatibilní s rozhraním označovaným jako V2_1) se vyžadují dvoustupňové upgrady. Postupujte podle kroků v této sekvenci.

Poznámka:

Při upgradu z V1 na V2 se ujistěte, že Remoting je obor názvů aktualizovaný tak, aby používal V2. Příklad: Microsoft.ServiceFabric.Services.Remoting.V2.FabricTransport.Client

  1. Upgradujte službu V1 na službu V2_1 pomocí následujícího atributu. Tato změna zajišťuje, že služba poslouchá na posluchači V1 a V2_1.

    a. Přidejte koncový bod s názvem "ServiceEndpointV2_1" v manifestu služby.

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

    b) K vytvoření posluchače vzdálené komunikace použijte následující metodu rozšíření.

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

    c. Přidejte atribut sestavení k rozhraním vzdálené komunikace, aby se použil naslouchací proces V1, V2_1 a klient V2_1.

    [assembly: FabricTransportServiceRemotingProvider(RemotingListenerVersion = RemotingListenerVersion.V2_1 | RemotingListenerVersion.V1, RemotingClientVersion = RemotingClientVersion.V2_1)]
    
    
  2. Upgradujte klienta V1 na klienta V2_1 pomocí atributu klienta V2_1. Tento krok zajistí, že klient používá stack V2_1. V klientském projektu nebo službě není nutná žádná změna. Vytváření klientských projektů s aktualizovaným sestavením rozhraní stačí.

  3. Tento krok je volitelný. Odeberte verzi posluchače V1 z atributu a následně upgradujte službu V2. Tento krok zajistí, že služba naslouchá pouze na posluchači V2.

    [assembly: FabricTransportServiceRemotingProvider(RemotingListenerVersion = RemotingListenerVersion.V2_1, RemotingClientVersion = RemotingClientVersion.V2_1)]
    

Použijte vlastní serializaci se zapouzdřenou zprávou pro vzdálenou komunikaci.

Pro zprávu obalenou pro vzdálenou komunikaci vytvoříme jediný obalený objekt, který obsahuje všechny parametry jako pole. Postupujte takto:

  1. IServiceRemotingMessageSerializationProvider Implementujte rozhraní, které poskytuje implementaci pro vlastní serializaci. Tento fragment kódu ukazuje, jak implementace vypadá.

    public class ServiceRemotingJsonSerializationProvider : IServiceRemotingMessageSerializationProvider
    {
      public IServiceRemotingMessageBodyFactory CreateMessageBodyFactory()
      {
        return new JsonMessageFactory();
      }
    
      public IServiceRemotingRequestMessageBodySerializer CreateRequestMessageSerializer(Type serviceInterfaceType, IEnumerable<Type> requestWrappedType, IEnumerable<Type> requestBodyTypes = null)
      {
        return new ServiceRemotingRequestJsonMessageBodySerializer();
      }
    
      public IServiceRemotingResponseMessageBodySerializer CreateResponseMessageSerializer(Type serviceInterfaceType, IEnumerable<Type> responseWrappedType, IEnumerable<Type> responseBodyTypes = null)
      {
        return new ServiceRemotingResponseJsonMessageBodySerializer();
      }
    }
    
      class JsonMessageFactory : IServiceRemotingMessageBodyFactory
          {
    
            public IServiceRemotingRequestMessageBody CreateRequest(string interfaceName, string methodName, int numberOfParameters, object wrappedRequestObject)
            {
              return new JsonBody(wrappedRequestObject);
            }
    
            public IServiceRemotingResponseMessageBody CreateResponse(string interfaceName, string methodName, object wrappedRequestObject)
            {
              return new JsonBody(wrappedRequestObject);
            }
          }
    
    class ServiceRemotingRequestJsonMessageBodySerializer : IServiceRemotingRequestMessageBodySerializer
      {
          private JsonSerializer serializer;
    
          public ServiceRemotingRequestJsonMessageBodySerializer()
          {
            serializer = JsonSerializer.Create(new JsonSerializerSettings()
            {
              TypeNameHandling = TypeNameHandling.All
              });
            }
    
            public IOutgoingMessageBody Serialize(IServiceRemotingRequestMessageBody serviceRemotingRequestMessageBody)
           {
             if (serviceRemotingRequestMessageBody == null)
             {
               return null;
             }          
             using (var writeStream = new MemoryStream())
             {
               using (var jsonWriter = new JsonTextWriter(new StreamWriter(writeStream)))
               {
                 serializer.Serialize(jsonWriter, serviceRemotingRequestMessageBody);
                 jsonWriter.Flush();
                 var bytes = writeStream.ToArray();
                 var segment = new ArraySegment<byte>(bytes);
                 var segments = new List<ArraySegment<byte>> { segment };
                 return new OutgoingMessageBody(segments);
               }
             }
            }
    
            public IServiceRemotingRequestMessageBody Deserialize(IIncomingMessageBody messageBody)
           {
             using (var sr = new StreamReader(messageBody.GetReceivedBuffer()))
             {
               using (JsonReader reader = new JsonTextReader(sr))
               {
                 var ob = serializer.Deserialize<JsonBody>(reader);
                 return ob;
               }
             }
           }
          }
    
    class ServiceRemotingResponseJsonMessageBodySerializer : IServiceRemotingResponseMessageBodySerializer
     {
       private JsonSerializer serializer;
    
      public ServiceRemotingResponseJsonMessageBodySerializer()
      {
        serializer = JsonSerializer.Create(new JsonSerializerSettings()
        {
            TypeNameHandling = TypeNameHandling.All
          });
        }
    
        public IOutgoingMessageBody Serialize(IServiceRemotingResponseMessageBody responseMessageBody)
        {
          if (responseMessageBody == null)
          {
            return null;
          }
    
          using (var writeStream = new MemoryStream())
          {
            using (var jsonWriter = new JsonTextWriter(new StreamWriter(writeStream)))
            {
              serializer.Serialize(jsonWriter, responseMessageBody);
              jsonWriter.Flush();
              var bytes = writeStream.ToArray();
              var segment = new ArraySegment<byte>(bytes);
              var segments = new List<ArraySegment<byte>> { segment };
              return new OutgoingMessageBody(segments);
            }
          }
        }
    
        public IServiceRemotingResponseMessageBody Deserialize(IIncomingMessageBody messageBody)
        {
    
           using (var sr = new StreamReader(messageBody.GetReceivedBuffer()))
           {
             using (var reader = new JsonTextReader(sr))
             {
               var obj = serializer.Deserialize<JsonBody>(reader);
               return obj;
             }
           }
         }
     }
    
    class JsonBody : WrappedMessage, IServiceRemotingRequestMessageBody, IServiceRemotingResponseMessageBody
    {
          public JsonBody(object wrapped)
          {
            this.Value = wrapped;
          }
    
          public void SetParameter(int position, string parameName, object parameter)
          {  //Not Needed if you are using WrappedMessage
            throw new NotImplementedException();
          }
    
          public object GetParameter(int position, string parameName, Type paramType)
          {
            //Not Needed if you are using WrappedMessage
            throw new NotImplementedException();
          }
    
          public void Set(object response)
          { //Not Needed if you are using WrappedMessage
            throw new NotImplementedException();
          }
    
          public object Get(Type paramType)
          {  //Not Needed if you are using WrappedMessage
            throw new NotImplementedException();
          }
    }
    
  2. Přepište výchozího zprostředkovatele serializace JsonSerializationProvider pro posluchač vzdálené komunikace.

    protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
    {
        return new[]
        {
            new ServiceInstanceListener((c) =>
            {
                return new FabricTransportServiceRemotingListener(context, _calculatorFactory.GetCalculator(Context), serializationProvider: new         ServiceRemotingJsonSerializationProvider());
            })
        };
    }
    
  3. Přepište výchozí nastavení zprostředkovatele JsonSerializationProvider serializace pro továrnu klienta pro vzdálenou komunikaci.

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

Další kroky