Héberger avec Service Fabric
Orleans peut être hébergé sur Azure Service Fabric à l’aide des packages NuGet Microsoft.ServiceFabric.Services et Microsoft.Orleans.Server. Les silos doivent être hébergés en tant que services non partitionnés et sans état, car Orleans gère lui-même la répartition des grains. Les autres options d’hébergement, telles que partitionné et avec état, sont plus complexes à mettre en œuvre et n’offrent aucun avantage sans personnalisation supplémentaire de la part du développeur. Pour Orleans, l’hébergement non partitionné et sans état est recommandé.
Service sans état Service Fabric en tant que silo
Que vous créiez une application Service Fabric ou que vous ajoutiez Orleans à une application existante, vous aurez besoin des références des packages Microsoft.ServiceFabric.Services
et Microsoft.Orleans.Server
dans votre projet. Le projet de service sans état a besoin d’une implémentation sur ICommunicationListener et d’une sous-classe de StatelessService.
Le cycle de vie du silo suit le cycle de vie de l’écouteur de communication classique :
- Il est initialisé avec ICommunicationListener.OpenAsync.
- Il est arrêté en douceur avec ICommunicationListener.CloseAsync.
- Ou il est arrêté brusquement avec ICommunicationListener.Abort.
Étant donné que les silos Orleans peuvent exister dans les limites de IHost, l’implémentation de ICommunicationListener
consiste à établir un wrapper autour de IHost
. Le IHost
est initialisé dans la méthode OpenAsync
et arrêté en douceur dans la méthode CloseAsync
:
ICommunicationListener |
Interactions IHost |
---|---|
OpenAsync | L’instance IHost est créée et un appel à StartAsync est effectué. |
CloseAsync | Un appel à StopAsync sur l’instance hôte est attendu. |
Abort | Un appel à StopAsync est évalué de force avec GetAwaiter().GetResult() . |
Prise en charge des clusters
La prise en charge officielle du clustering est disponible à partir de divers packages, notamment :
- Microsoft.Orleans.Clustering.AzureStorage
- Microsoft.Orleans.Clustering.AdoNet
- Microsoft.Orleans.Clustering.DynamoDB
Il existe également plusieurs packages tiers disponibles pour d’autres services tels que CosmosDB, Kubernetes, Redis et Aerospike. Pour plus d’informations, consultez Gestion de cluster dans Orleans.
Exemple de projet
Dans le projet de service sans état, implémentez l’interface ICommunicationListener
comme indiqué dans l’exemple suivant :
using Microsoft.Extensions.Hosting;
using Microsoft.ServiceFabric.Services.Communication.Runtime;
namespace ServiceFabric.HostingExample;
internal sealed class HostedServiceCommunicationListener : ICommunicationListener
{
private IHost? _host;
private readonly Func<Task<IHost>> _createHost;
public HostedServiceCommunicationListener(Func<Task<IHost>> createHost) =>
_createHost = createHost ?? throw new ArgumentNullException(nameof(createHost));
/// <inheritdoc />
public async Task<string?> OpenAsync(CancellationToken cancellationToken)
{
try
{
_host = await _createHost.Invoke();
await _host.StartAsync(cancellationToken);
}
catch
{
Abort();
throw;
}
// This service does not expose any endpoints to Service Fabric for discovery by others.
return null;
}
/// <inheritdoc />
public async Task CloseAsync(CancellationToken cancellationToken)
{
if (_host is { } host)
{
await host.StopAsync(cancellationToken);
}
_host = null;
}
/// <inheritdoc />
public void Abort()
{
IHost? host = _host;
if (host is null)
{
return;
}
using CancellationTokenSource cancellation = new();
cancellation.Cancel(false);
try
{
host.StopAsync(cancellation.Token).GetAwaiter().GetResult();
}
catch
{
// Ignore.
}
finally
{
_host = null;
}
}
}
La classe HostedServiceCommunicationListener
accepte un paramètre de constructeur Func<Task<IHost>> createHost
. Elle est ensuite utilisée pour créer l’instance IHost
dans la méthode OpenAsync
.
La partie suivante du projet de service sans état consiste à implémenter la classe StatelessService
. L’exemple suivant montre la sous-classe de la classe StatelessService
:
using System.Fabric;
using Microsoft.Extensions.Hosting;
using Microsoft.ServiceFabric.Services.Communication.Runtime;
using Microsoft.ServiceFabric.Services.Runtime;
namespace ServiceFabric.HostingExample;
public sealed class OrleansHostedStatelessService : StatelessService
{
private readonly Func<StatelessServiceContext, Task<IHost>> _createHost;
public OrleansHostedStatelessService(
Func<StatelessServiceContext, Task<IHost>> createHost, StatelessServiceContext serviceContext)
: base(serviceContext) =>
_createHost = createHost ?? throw new ArgumentNullException(nameof(createHost));
/// <inheritdoc/>
protected sealed override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
{
// Create a listener which creates and runs an IHost
yield return new ServiceInstanceListener(
context => new HostedServiceCommunicationListener(() => _createHost(context)),
nameof(HostedServiceCommunicationListener));
}
}
Dans l’exemple précédent, la classe OrleansHostedStatelessService
est chargée de générer une instance ICommunicationListener
. La méthode CreateServiceInstanceListeners
est appelée par le runtime Service Fabric lors de l’initialisation du service.
En regroupant ces deux classes, l’exemple suivant montre le fichier program.cs complet du projet de service sans état :
using System.Fabric;
using Microsoft.Extensions.Hosting;
using Microsoft.ServiceFabric.Services.Runtime;
using ServiceFabric.HostingExample;
try
{
// The ServiceManifest.XML file defines one or more service type names.
// Registering a service maps a service type name to a .NET type.
// When Service Fabric creates an instance of this service type,
// an instance of the class is created in this host process.
await ServiceRuntime.RegisterServiceAsync(
"Orleans.ServiceFabric.Stateless",
context => new OrleansHostedStatelessService(
CreateHostAsync, context));
ServiceEventSource.Current.ServiceTypeRegistered(
Environment.ProcessId,
typeof(OrleansHostedStatelessService).Name);
// Prevents this host process from terminating so services keep running.
await Task.Delay(Timeout.Infinite);
}
catch (Exception ex)
{
ServiceEventSource.Current.ServiceHostInitializationFailed(
ex.ToString());
throw;
}
static async Task<IHost> CreateHostAsync(StatelessServiceContext context)
{
await Task.CompletedTask;
return Host.CreateDefaultBuilder()
.UseOrleans((_, builder) =>
{
// TODO, Use real storage, something like table storage
// or SQL Server for clustering.
builder.UseLocalhostClustering();
// Service Fabric manages port allocations, so update the
// configuration using those ports. Gather configuration from
// Service Fabric.
var activation = context.CodePackageActivationContext;
var endpoints = activation.GetEndpoints();
// These endpoint names correspond to TCP endpoints
// specified in ServiceManifest.xml
var siloEndpoint = endpoints["OrleansSiloEndpoint"];
var gatewayEndpoint = endpoints["OrleansProxyEndpoint"];
var hostname = context.NodeContext.IPAddressOrFQDN;
builder.ConfigureEndpoints(hostname,
siloEndpoint.Port, gatewayEndpoint.Port);
})
.Build();
}
Dans le code précédent :
- La méthodeServiceRuntime.RegisterServiceAsync enregistre la classe
OrleansHostedStatelessService
auprès du runtime Service Fabric. - Le délégué
CreateHostAsync
permet de créer l’instanceIHost
.