Szolgáltatásátírás C#-ban a Reliable Services használatával

Az olyan szolgáltatások esetében, amelyek nem kapcsolódnak egy adott kommunikációs protokollhoz vagy veremhez, például egy webes API-hoz, a Windows Communication Foundationhez vagy más szolgáltatásokhoz, a Reliable Services keretrendszer egy újraírási mechanizmust biztosít a szolgáltatások távoli eljáráshívásainak gyors és egyszerű beállításához. Ez a cikk a C#-tal írt szolgáltatások távoli eljáráshívásainak beállítását ismerteti.

Szolgáltatáson történő újraegyenlítés beállítása

A szolgáltatás újraegyenlítését két egyszerű lépésben állíthatja be:

  1. Hozzon létre egy felületet a szolgáltatás implementálásához. Ez a felület határozza meg azokat a metódusokat, amelyek a szolgáltatáson keresztüli távoli eljáráshívásokhoz érhetők el. A metódusnak feladat-visszaadó aszinkron metódusnak kell lennie. Az interfésznek implementálnia Microsoft.ServiceFabric.Services.Remoting.IService kell, hogy jelezhesse, hogy a szolgáltatás rendelkezik remoting interface-rel.
  2. Használjon újraegyenesítési figyelőt a szolgáltatásban. Az újraegyenlítési figyelő olyan ICommunicationListener implementáció, amely újraegyeztetési képességeket biztosít. A Microsoft.ServiceFabric.Services.Remoting.Runtime névtér tartalmazza az állapot nélküli és az állapotalapú szolgáltatások bővítménymetódusát CreateServiceRemotingInstanceListeners is, amellyel az alapértelmezett átviteli protokoll használatával létrehozhat újraegyeztetési figyelőt.

Megjegyzés

A Remoting névtér egy különálló NuGet-csomagként érhető el.Microsoft.ServiceFabric.Services.Remoting

Az alábbi állapot nélküli szolgáltatás például egyetlen metódust tesz elérhetővé a ""Helló világ!" alkalmazás" távoli eljáráshíváson keresztüli lekéréséhez.

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

Megjegyzés

A szolgáltatásfelület argumentumai és visszatérési típusai lehetnek egyszerű, összetett vagy egyéni típusok, de a .NET DataContractSerializernek képesnek kell lennie szerializálni őket.

Távoli szolgáltatás metódusok hívása

Megjegyzés

Ha egynél több partíciót használ, a ServiceProxy.Create() paraméternek meg kell adnia a megfelelő ServicePartitionKey értéket. Erre nincs szükség egy partíciós forgatókönyv esetén.

A szolgáltatás metódusainak hívása az újraegyenlítési verem használatával történik egy helyi proxyval a szolgáltatáshoz az Microsoft.ServiceFabric.Services.Remoting.Client.ServiceProxy osztályon keresztül. A ServiceProxy metódus a szolgáltatás által implementálható felülettel hoz létre egy helyi proxyt. Ezzel a proxyval távolról hívhatja meg a metódusokat a felületen.


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

string message = await helloWorldClient.HelloWorldAsync();

Az újraegyenlítési keretrendszer propagálja a szolgáltatás által az ügyfélnek kiadott kivételeket. Ennek eredményeképpen használat esetén ServiceProxyaz ügyfél felelős a szolgáltatás által kidobott kivételek kezeléséért.

Szolgáltatásproxy élettartama

A szolgáltatásproxy létrehozása egyszerű művelet, így tetszőleges számúat hozhat létre. A szolgáltatásproxy-példányok mindaddig újra felhasználhatók, amíg szükség van rájuk. Ha egy távoli eljáráshívás kivételt jelez, továbbra is felhasználhatja ugyanazt a proxypéldányt. Minden szolgáltatásproxy tartalmaz egy kommunikációs ügyfelet, amellyel üzeneteket küldhet a vezetéken keresztül. A távoli hívások meghívása közben belső ellenőrzéseket hajtanak végre annak megállapításához, hogy a kommunikációs ügyfél érvényes-e. Az ellenőrzések eredményei alapján a kommunikációs ügyfél szükség esetén újra létrejön. Ezért kivétel esetén nem kell újból létrehoznia a következőt ServiceProxy: .

Szolgáltatásproxy-előállító élettartama

A ServiceProxyFactory egy olyan gyár, amely proxypéldányokat hoz létre a különböző újrametszési felületekhez. Ha az API-val ServiceProxyFactory.CreateServiceProxy hoz létre proxyt, a keretrendszer létrehoz egy egyszeri szolgáltatásproxyt. Hasznos manuálisan létrehozni egyet, ha felül kell bírálnia az IServiceRemotingClientFactory tulajdonságait. A gyár létrehozása költséges művelet. A szolgáltatásproxy-előállító fenntartja a kommunikációs ügyfél belső gyorsítótárát. Ajánlott eljárás a szolgáltatásproxy-előállító gyorsítótárazása a lehető leghosszabb ideig.

Kivételkezelés újraegyezése

A szolgáltatás API által küldött összes távoli kivételt a rendszer AggregateException néven küldi vissza az ügyfélnek. A távoli kivételeket a DataContract szerializálhatja. Ha nem, a proxy API a ServiceExceptionet a benne lévő szerializálási hibával dobja.

A szolgáltatásproxy kezeli a létrehozott szolgáltatáspartíció összes feladatátvételi kivételét. Újra feloldja a végpontokat, ha vannak feladatátvételi kivételek (nem átmeneti kivételek), és újrapróbálja a hívást a megfelelő végponttal. A feladatátvételi kivételek újrapróbálkozási száma határozatlan. Átmeneti kivételek esetén a proxy újrapróbálkozásokat kezdeményez a hívással.

Az alapértelmezett újrapróbálkozási paramétereket az OperationRetrySettings biztosítja.

A felhasználók úgy konfigurálhatják ezeket az értékeket, hogy átadják az OperationRetrySettings objektumot a ServiceProxyFactory konstruktornak.

A V2-verem újraegyenlítésének használata

A NuGet-remoting csomag 2.8-as verziójától már használhatja a V2-vermet. A V2-verem újrahangolása jobb teljesítményt mutat. Emellett olyan funkciókat is biztosít, mint az egyéni szerializálás és a csatlakoztatható API-k. A sablonkód továbbra is az 1. V1-vermet használja. A V2-remoting nem kompatibilis az 1. V-vel (az előző újraegyenlítési verem). A szolgáltatás rendelkezésre állására gyakorolt hatások elkerülése érdekében kövesse a Frissítés 1-ről v2-re című cikkben található utasításokat.

A V2-verem engedélyezéséhez az alábbi megközelítések érhetők el.

Szerelvényattribútum használata a V2 verem használatához

Ezek a lépések egy szerelvényattribútum használatával módosítják a sablonkódot a V2 verem használatára.

  1. Módosítsa a végponterőforrást "ServiceEndpoint" a szolgáltatásjegyzékben lévőre "ServiceEndpointV2" .

    <Resources>
     <Endpoints>
       <Endpoint Name="ServiceEndpointV2" />
     </Endpoints>
    </Resources>
    
  2. Microsoft.ServiceFabric.Services.Remoting.Runtime.CreateServiceRemotingInstanceListeners A bővítménymetódus használatával hozzon létre újraegyenesítési figyelőket (ez az 1. és a 2. v. verzió esetében is egyenlő).

     protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
     {
         return this.CreateServiceRemotingInstanceListeners();
     }
    
  3. Jelölje meg azt a szerelvényt, amely az újraegyenlítési illesztőket tartalmazza egy FabricTransportServiceRemotingProvider attribútummal.

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

Az ügyfélprojektben nincs szükség kódmódosításra. Hozza létre az ügyfélszerelvényt az illesztőszerelvénysel, és győződjön meg arról, hogy a korábban bemutatott szerelvényattribútumot használja.

Explicit V2-osztályok használata a V2 verem használatához

A szerelvényattribútumok használata helyett a V2 verem explicit V2-osztályokkal is engedélyezhető.

Ezek a lépések explicit V2-osztályok használatával módosítják a sablonkódot a V2 verem használatára.

  1. Módosítsa a végponterőforrást "ServiceEndpoint" a szolgáltatásjegyzékben lévőre "ServiceEndpointV2" .

    <Resources>
     <Endpoints>
       <Endpoint Name="ServiceEndpointV2" />
     </Endpoints>
    </Resources>
    
  2. Használja a FabricTransportServiceRemotingListenert a Microsoft.ServiceFabric.Services.Remoting.V2.FabricTransport.Runtime névtérből.

    protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
     {
         return new[]
         {
             new ServiceInstanceListener((c) =>
             {
                 return new FabricTransportServiceRemotingListener(c, this);
    
             })
         };
     }
    
  3. Az ügyfelek létrehozásához használja a FabricTransportServiceRemotingClientFactory parancsot a Microsoft.ServiceFabric.Services.Remoting.V2.FabricTransport.Client névtérből.

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

Frissítés az 1. virtuális gépről a V2 újraegyesítésére

A V1-ről v2-re való frissítéshez kétlépéses frissítésre van szükség. Kövesse a sorozat lépéseit.

  1. Ezzel az attribútummal frissítse a V1 szolgáltatást v2 szolgáltatásra. Ez a módosítás biztosítja, hogy a szolgáltatás figyeli a V1 és a V2 figyelőt.

    a. Adjon hozzá egy "ServiceEndpointV2" nevű végponterőforrást a szolgáltatásjegyzékben.

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

    b. Az alábbi bővítménymetódussal hozzon létre újraegyesítő figyelőt.

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

    c. Adjon hozzá egy szerelvényattribútumot az újraegyenlítő felületekhez a V1 és a V2 figyelő és a V2-ügyfél használatához.

    [assembly: FabricTransportServiceRemotingProvider(RemotingListenerVersion = RemotingListenerVersion.V2|RemotingListenerVersion.V1, RemotingClientVersion = RemotingClientVersion.V2)]
    
    
  2. Frissítse a V1-ügyfelet egy V2-ügyfélre a V2 ügyfélattribútum használatával. Ez a lépés biztosítja, hogy az ügyfél a V2 vermet használja. Az ügyfélprojektben/szolgáltatásban nincs szükség módosításra. Elegendő az ügyfélprojektek létrehozása frissített illesztőszerelvénysel.

  3. Ez a lépés nem kötelező. Használja a V2 figyelő attribútumot, majd frissítse a V2 szolgáltatást. Ez a lépés biztosítja, hogy a szolgáltatás csak a V2-figyelőn figyeljen.

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

A remoting V2 (interfészkompatibilis) verem használata

A V2 -remoting V2 (interfészkompatibilis) verem V2_1 néven ismert, és a legújabb verzió. A V2-remoting verem összes funkcióját tartalmazza. A felületi verem kompatibilis az 1. virtuális verem újraegyeztetésével, de nem kompatibilis a V2-vel és a V1-zel. Ha az 1-ről a V2_1-ra szeretne frissíteni a szolgáltatás rendelkezésre állásának befolyásolása nélkül, kövesse a Frissítés V1-ről V2-re (felületkompatibilis) című cikk lépéseit.

Szerelvényattribútum használata a remoting V2 (interfészkompatibilis) verem használatához

Az alábbi lépéseket követve váltson V2_1 veremre.

  1. Adjon hozzá egy "ServiceEndpointV2_1" nevű végponterőforrást a szolgáltatásjegyzékben.

    <Resources>
     <Endpoints>
       <Endpoint Name="ServiceEndpointV2_1" />  
     </Endpoints>
    </Resources>
    
  2. A távelérési bővítmény metódusával hozzon létre egy újrametódus-figyelőt.

     protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
     {
         return this.CreateServiceRemotingInstanceListeners();
     }
    
  3. Adjon hozzá egy szerelvényattribútumot az újraegyenlítő felületekhez.

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

Az ügyfélprojektben nincs szükség módosításokra. Hozza létre az ügyfélszerelvényt az illesztőszerelvénylel, és győződjön meg arról, hogy az előző szerelvényattribútumot használja.

Explicit átnevezési osztályok használata figyelő/ügyfél-előállító létrehozásához a V2 (interfészkompatibilis) verzióhoz

Kövesse az alábbi lépéseket:

  1. Adjon hozzá egy "ServiceEndpointV2_1" nevű végponterőforrást a szolgáltatásjegyzékben.

    <Resources>
     <Endpoints>
       <Endpoint Name="ServiceEndpointV2_1" />  
     </Endpoints>
    </Resources>
    
  2. Használja a V2-figyelőt. A használt alapértelmezett szolgáltatásvégpont-erőforrásnév a "ServiceEndpointV2_1". Ezt a szolgáltatásjegyzékben kell meghatározni.

    protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
     {
         return new[]
         {
             new ServiceInstanceListener((c) =>
             {
                 var settings = new FabricTransportRemotingListenerSettings();
                 settings.UseWrappedMessage = true;
                 return new FabricTransportServiceRemotingListener(c, this,settings);
    
             })
         };
     }
    
  3. Használja a V2 ügyfél-előállítót.

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

Frissítés a V1-ről a V2-re (felületkompatibilis)

A V1-ről a V2-re való frissítéshez (interfészkompatibilis, más néven V2_1) kétlépéses frissítésre van szükség. Kövesse az ebben a sorrendben leírt lépéseket.

Megjegyzés

A V1-ről a V2-re való frissítéskor győződjön meg arról, hogy a Remoting névtér frissült a V2 használatára. Például: Microsoft.ServiceFabric.Services.Remoting.V2.FabricTransport.Client

  1. Frissítse a V1 szolgáltatást V2_1 szolgáltatásra az alábbi attribútum használatával. Ez a módosítás gondoskodik arról, hogy a szolgáltatás figyeli a V1-et és a V2_1 figyelőt.

    a. Adjon hozzá egy "ServiceEndpointV2_1" nevű végponterőforrást a szolgáltatásjegyzékben.

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

    b. A következő bővítménymetódussal hozzon létre egy újrametódus-figyelőt.

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

    c. Adjon hozzá egy szerelvényattribútumot a virtuális 1-et, V2_1 figyelőt és V2_1-ügyfelet használó újraegyeztetési felületekhez.

    [assembly: FabricTransportServiceRemotingProvider(RemotingListenerVersion = RemotingListenerVersion.V2_1 | RemotingListenerVersion.V1, RemotingClientVersion = RemotingClientVersion.V2_1)]
    
    
  2. Frissítse a V1-ügyfelet a V2_1-ügyfélre a V2_1 ügyfélattribútum használatával. Ez a lépés gondoskodik arról, hogy az ügyfél a V2_1 vermet használja. Az ügyfélprojektben/szolgáltatásban nincs szükség módosításra. A frissített illesztőszerelvényt tartalmazó ügyfélprojektek létrehozása elegendő.

  3. Ez a lépés nem kötelező. Távolítsa el a V1 figyelő verzióját az attribútumból, majd frissítse a V2 szolgáltatást. Ez a lépés biztosítja, hogy a szolgáltatás csak a V2-figyelőt figyelje.

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

Egyéni szerializálás használata újraküldéses burkolt üzenettel

A burkolt üzenetek visszaküldéséhez egyetlen burkolt objektumot hozunk létre, amelyben az összes paraméter mezőként szerepel. Kövesse az alábbi lépéseket:

  1. Implementálja a IServiceRemotingMessageSerializationProvider felületet az egyéni szerializálás implementálásának biztosításához. Ez a kódrészlet bemutatja, hogyan néz ki az implementáció.

    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. Felülbírálja az alapértelmezett szerializálási szolgáltatót a következővel JsonSerializationProvider : egy újraindító figyelőhöz.

    protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
    {
        return new[]
        {
            new ServiceInstanceListener((c) =>
            {
                return new FabricTransportServiceRemotingListener(context, _calculatorFactory.GetCalculator(Context), serializationProvider: new         ServiceRemotingJsonSerializationProvider());
            })
        };
    }
    
  3. Felülbírálja az alapértelmezett szerializálási szolgáltatót a következővel JsonSerializationProvider egy újraegyesítő ügyfél-előállító esetében.

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

Következő lépések