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:
- 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. - 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. AMicrosoft.ServiceFabric.Services.Remoting.Runtime
névtér tartalmazza az állapot nélküli és az állapotalapú szolgáltatások bővítménymetódusátCreateServiceRemotingInstanceListeners
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 ServiceProxy
az ü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.
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>
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(); }
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.
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>
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); }) }; }
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.
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)]
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.
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.
Adjon hozzá egy "ServiceEndpointV2_1" nevű végponterőforrást a szolgáltatásjegyzékben.
<Resources> <Endpoints> <Endpoint Name="ServiceEndpointV2_1" /> </Endpoints> </Resources>
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(); }
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:
Adjon hozzá egy "ServiceEndpointV2_1" nevű végponterőforrást a szolgáltatásjegyzékben.
<Resources> <Endpoints> <Endpoint Name="ServiceEndpointV2_1" /> </Endpoints> </Resources>
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); }) }; }
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
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)]
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ő.
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:
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(); } }
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()); }) }; }
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()); });