Sdílet prostřednictvím


Vzdálená komunikace služby v jazyce C# s Reliable Services

Pro služby, které nejsou svázané 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é komunikace pro rychlé a snadné nastavení vzdálených volání procedur 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ě

Vzdálené komunikace pro službu můžete nastavit 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, aby signalizovalo Microsoft.ServiceFabric.Services.Remoting.IService , že služba má rozhraní vzdálené komunikace.
  2. Ve službě použijte naslouchací proces vzdálené komunikace. Naslouchací proces vzdálené komunikace je ICommunicationListener implementace, která poskytuje možnosti vzdálené komunikace. Microsoft.ServiceFabric.Services.Remoting.Runtime Obor názvů obsahuje metodu CreateServiceRemotingInstanceListeners rozšíření pro bezstavové i stavové služby, které lze použít k vytvoření naslouchacího procesu vzdálené komunikace pomocí výchozího přenosového protokolu vzdálené komunikace.

Poznámka:

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

Například následující bezstavová služba zveřejňuje jednu metodu pro 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 jeden scénář oddílu.

Volání metod ve službě pomocí zásobníku vzdálené komunikace se provádí pomocí místního proxy serveru do služby prostřednictvím Microsoft.ServiceFabric.Services.Remoting.Client.ServiceProxy třídy. 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 šíří výjimky vyvolané službou do klienta. V důsledku toho je ServiceProxyklient zodpovědný za zpracování výjimek vyvolaných službou.

Ž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 vzdálené komunikace

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 možné serializovat dataContract. Pokud tomu tak není, rozhraní API proxy vyvolá výjimku ServiceException s chybou serializace.

Proxy služby zpracovává všechny výjimky převzetí služeb při selhání pro oddíl služby, pro který se vytvoří. 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řevzetí služeb 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 vzdálené komunikace NuGet máte možnost použít zásobník vzdálené komunikace V2. Zásobník vzdálené komunikace V2 funguje lépe. Poskytuje také funkce, jako je vlastní serializace a další připojitelná rozhraní API. Kód šablony nadále používá zásobník vzdálené komunikace V1. Vzdálenou komunikace V2 není kompatibilní s V1 (předchozí zásobník vzdálené komunikace). Postupujte podle pokynů v článku Upgrade z V1 na V2 , abyste se vyhnuli dopadům na dostupnost služeb.

Pro povolení zásobníku V2 jsou k dispozici následující přístupy.

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

Tyto kroky změní kód šablony tak, aby používal zásobník V2 pomocí atributu sestavení.

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

    <Resources>
     <Endpoints>
       <Endpoint Name="ServiceEndpointV2" />
     </Endpoints>
    </Resources>
    
  2. Microsoft.ServiceFabric.Services.Remoting.Runtime.CreateServiceRemotingInstanceListeners Pomocí metody rozšíření vytvořte naslouchací procesy vzdálené komunikace (stejné pro V1 i V2).

     protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
     {
         return this.CreateServiceRemotingInstanceListeners();
     }
    
  3. Označte sestavení, které obsahuje rozhraní vzdálené komunikace 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 prostředek koncového bodu z "ServiceEndpoint" manifestu služby 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 vzdálené komunikace V1 na vzdálené komunikace 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 naslouchala naslouchacím procesu 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 sestavení pro rozhraní vzdálené komunikace pro použití naslouchacího procesu 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 nepovinný. Použijte atribut naslouchacího procesu V2 a pak upgradujte službu V2. Tento krok zajistí, že služba naslouchá pouze na naslouchacím procesu V2.

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

Použití zásobníku 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 nejaktuálnější verzí. Má všechny funkce zásobníku 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žití atributu sestavení k použití zásobníku vzdálené komunikace V2 (kompatibilní s rozhraním)

Podle těchto kroků přejděte na zásobník 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žití explicitních tříd vzdálené komunikace k vytvoření naslouchacího procesu nebo klientské továrny pro verzi V2 (kompatibilní s rozhraním)

Postupujte následovně:

  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 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 z vzdálené komunikace V1 na vzdálenou komunikace 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 naslouchá na v1 a naslouchacím procesu V2_1.

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

    <Resources>
      <Endpoints>
        <Endpoint Name="ServiceEndpointV2_1" />  
      </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 sestavení pro rozhraní vzdálené komunikace pro použití klienta V1, V2_1 naslouchacího procesu a V2_1 klienta.

    [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á zásobník 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 nepovinný. Odeberte verzi naslouchacího procesu V1 z atributu a pak upgradujte službu V2. Tento krok zajistí, že služba naslouchá pouze na naslouchacím procesu V2.

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

Použití vlastní serializace se zalomenou zprávou vzdálené komunikace

U zprávy zabalené do vzdálené komunikace vytvoříme jeden zabalený objekt se všemi parametry jako pole. Postupujte následovně:

  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 JsonSerializationProvider serializace pro naslouchací proces 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ího zprostředkovatele JsonSerializationProvider serializace pro klientskou továrnu vzdálené komunikace.

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

Další kroky