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

Pro služby, které nejsou vázané na konkrétní komunikační protokol nebo zásobník, jako je webové rozhraní API, Windows Communication Foundation nebo jiné, poskytuje architektura Reliable Services mechanismus vzdálené komunikace, který umožňuje rychle a snadno nastavit volání vzdálených procedur pro služby. Tento článek popisuje, jak nastavit vzdálená volání procedur pro služby napsané v jazyce 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í, které má vaše služba implementovat. Toto rozhraní definuje metody, které jsou k dispozici pro vzdálené volání procedur ve vaší službě. Metody musí být asynchronní metody vracející úlohy. Rozhraní musí být implementováno Microsoft.ServiceFabric.Services.Remoting.IService , aby signalizovalo, že služba má rozhraní vzdálené komunikace.
  2. Použijte ve službě naslouchací proces vzdálené komunikace. Naslouchací proces vzdálené komunikace je ICommunicationListener implementace, která poskytuje možnosti vzdálené komunikace. Obor Microsoft.ServiceFabric.Services.Remoting.Runtime názvů obsahuje rozšiřující metodu CreateServiceRemotingInstanceListeners pro bezstavové i stavové služby, kterou lze použít k vytvoření naslouchacího procesu vzdálené komunikace pomocí výchozího protokolu přenosu 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 pomocí .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 odpovídající ServicePartitionKey. To není potřeba pro scénář s jedním oddílem.

Volání metod ve službě pomocí zásobníku vzdálené komunikace se provádí pomocí místního proxy serveru pro službu 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 ServiceProxyje klient 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 jich můžete vytvořit tolik, kolik potřebujete. Instance proxy služby je možné opakovaně používat tak dlouho, dokud jsou potřeba. Pokud volání vzdálené procedury vyvolá výjimku, můžete znovu použít stejnou instanci proxy serveru. Každý proxy server služby obsahuje komunikačního klienta, který se používá k odesílání zpráv prostřednictvím přenosu. Při volání vzdálených volání se provádějí 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, nemusíte znovu vytvářet ServiceProxy.

Životnost služby proxy factory

ServiceProxyFactory je továrna, která vytváří instance proxy pro různá rozhraní vzdálené komunikace. Pokud k vytvoření proxy používáte rozhraní API ServiceProxyFactory.CreateServiceProxy , rozhraní vytvoří jednoúčelový proxy server služby. Pokud potřebujete přepsat vlastnosti IServiceRemotingClientFactory, je užitečné ji vytvořit ručně. Vytvoř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 objekt proxy služby do mezipaměti 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 do klienta jako AggregateException. Vzdálené výjimky by měly být možné serializovat pomocí DataContract. Pokud tomu tak není, rozhraní API proxy vyvolá výjimku ServiceException s chybou serializace v ní.

Proxy server služby zpracovává všechny výjimky převzetí služeb při selhání pro oddíl služby, pro který je vytvořený. Přeloží koncové body, pokud existují výjimky převzetí služeb při selhání (jiné než přechodné výjimky) a znovu se pokusí o 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 Objekt OperationRetrySettings konstruktoru ServiceProxyFactory.

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

Od verze 2.8 vzdálené komunikace balíčku 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 dál používá zásobník vzdálené komunikace V1. Vzdálené komunikace V2 není kompatibilní s V1 (předchozí zásobník vzdálené komunikace). Pokud se chcete vyhnout dopadům na dostupnost služby, postupujte podle pokynů v článku Upgrade z verze 1 na verzi 2 .

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

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

Tento postup 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" na "ServiceEndpointV2" v manifestu služby.

    <Resources>
     <Endpoints>
       <Endpoint Name="ServiceEndpointV2" />
     </Endpoints>
    </Resources>
    
  2. Microsoft.ServiceFabric.Services.Remoting.Runtime.CreateServiceRemotingInstanceListeners Pomocí rozšiřující metody 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, atributem FabricTransportServiceRemotingProvider .

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

V klientském projektu nejsou vyžadovány žádné změny kódu. Sestavte sestavení klienta pomocí sestavení rozhraní, abyste se ujistili, že je použit 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" na "ServiceEndpointV2" v manifestu služby.

    <Resources>
     <Endpoints>
       <Endpoint Name="ServiceEndpointV2" />
     </Endpoints>
    </Resources>
    
  2. Z oboru názvů použijte FabricTransportServiceRemotingListenerMicrosoft.ServiceFabric.Services.Remoting.V2.FabricTransport.Runtime.

    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 ze vzdálené komunikace V1 na vzdálené komunikace V2

Pokud chcete upgradovat z verze V1 na V2, je potřeba provést dvoustupňové upgrady. Postupujte podle kroků v tomto pořadí.

  1. Pomocí tohoto atributu upgradujte službu V1 na službu V2. Tato změna zajistí, že služba naslouchá naslouchacím procesu V1 a V2.

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

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

    b. Pomocí následující rozšiřující metody vytvořte naslouchací proces vzdálené komunikace.

    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 verze 2. V projektu nebo službě klienta se nevyžaduje žádná změna. Stačí vytvářet klientské projekty s aktualizovaným sestavením rozhraní.

  3. Tento krok je volitelný. Použijte atribut naslouchacího procesu V2 a pak upgradujte službu V2. Tento krok zajistí, aby služba naslouchala jenom na naslouchacím procesu verze 2.

    [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 verzemi V2 a V1. Pokud chcete upgradovat z verze V1 na V2_1, aniž by to mělo vliv na dostupnost služby, postupujte podle kroků v článku Upgrade z verze 1 na verzi 2 (kompatibilní s rozhraním).

Použití atributu sestavení k použití zásobníku vzdálené komunikace V2 (kompatibilní s rozhraním)

Pokud chcete přejít na zásobník V2_1, postupujte podle těchto kroků.

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

    <Resources>
     <Endpoints>
       <Endpoint Name="ServiceEndpointV2_1" />  
     </Endpoints>
    </Resources>
    
  2. K vytvoření naslouchacího procesu vzdálené komunikace použijte rozšiřující metodu 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 projektu klienta nejsou vyžadovány žádné změny. Sestavte sestavení klienta pomocí sestavení rozhraní, abyste se ujistili, že se používá předchozí atribut sestavení.

Použití explicitních tříd vzdálené komunikace k vytvoření objektu pro vytváření naslouchacího procesu nebo klienta pro verzi V2 (kompatibilní s rozhraním)

Postupujte takto:

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

    <Resources>
     <Endpoints>
       <Endpoint Name="ServiceEndpointV2_1" />  
     </Endpoints>
    </Resources>
    
  2. Použijte naslouchací proces vzdálené komunikace V2. Výchozí použitý 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 objekt pro vytváření klienta V2.

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

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

Pokud chcete upgradovat z verze V1 na V2 (kompatibilní s rozhraním, označované jako V2_1), je potřeba provést dvoustupňové upgrady. Postupujte podle kroků v tomto pořadí.

Poznámka

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

  1. Pomocí následujícího atributu upgradujte službu V1 na službu V2_1. Tato změna zajistí, že služba naslouchá naslouchacím procesu verze 1 a V2_1.

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

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

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

    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, V2_1 a klienta V2_1.

    [assembly: FabricTransportServiceRemotingProvider(RemotingListenerVersion = RemotingListenerVersion.V2_1 | RemotingListenerVersion.V1, RemotingClientVersion = RemotingClientVersion.V2_1)]
    
    
  2. Upgradujte klienta verze 1 na klienta V2_1 pomocí atributu V2_1 klienta. Tento krok zajistí, že klient používá zásobník V2_1. Není nutná žádná změna v projektu nebo službě klienta. Sestavení klientských projektů s aktualizovaným sestavením rozhraní je dostačující.

  3. Tento krok je volitelný. Odeberte verzi naslouchacího procesu V1 z atributu a pak upgradujte službu V2. Tento krok zajistí, že služba naslouchá jenom naslouchacím procesu verze 2.

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

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

U zprávy zabalené pomocí vzdálené komunikace vytvoříme jeden zabalený objekt se všemi parametry jako polem. Postupujte takto:

  1. IServiceRemotingMessageSerializationProvider Implementujte rozhraní pro poskytování implementace 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 pro JsonSerializationProvider 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 serializace pomocí JsonSerializationProvider pro objekt pro vytváření vzdálené komunikace klienta.

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

Další kroky