Host con Service Fabric
Orleans può essere ospitato in Azure Service Fabric usando i pacchetti NuGet Microsoft.ServiceFabric.Services e Microsoft.Orleans. Server. I silo devono essere ospitati come servizi senza stato, poiché Orleans gestisce la distribuzione dei grani stessi. Altre opzioni di hosting, ad esempio partizionate e con stato, sono più complesse e non offrono alcun vantaggio senza alcuna personalizzazione aggiuntiva da parte dello sviluppatore. È consigliabile ospitare Orleans senza stato e senza partizionamento.
Servizio senza stato di Service Fabric come silo
Sia che si stia creando una nuova applicazione Service Fabric, sia che si stia aggiungendo Orleans a una esistente, è necessario avere i riferimenti ai pacchetti Microsoft.ServiceFabric.Services
e Microsoft.Orleans.Server
nel progetto. Il progetto di servizio senza stato richiede un'implementazione in ICommunicationListener e una sottoclasse dell'oggetto StatelessService.
Il ciclo di vita del silo segue il ciclo di vita tipico del listener di comunicazione:
- Viene inizializzato con ICommunicationListener.OpenAsync.
- È terminato normalmente con ICommunicationListener.CloseAsync.
- In alternativa, è terminato bruscamente con ICommunicationListener.Abort.
Poiché i silo Orleans sono in grado di vivere entro i confini di IHost, l'implementazione di ICommunicationListener
è un wrapper intorno a IHost
. L'oggetto IHost
viene inizializzato nel metodo OpenAsync
e terminato normalmente nel metodo CloseAsync
:
ICommunicationListener |
InterazioniIHost |
---|---|
OpenAsync | Viene creata l'istanza IHost e viene effettuata una chiamata a StartAsync. |
CloseAsync | È attesa una chiamata a StopAsync nell'istanza host. |
Abort | Una chiamata a StopAsync viene valutata in modo forzato, con GetAwaiter().GetResult() . |
Supporto di cluster
Il supporto ufficiale per il clustering è disponibile da vari pacchetti, tra cui:
- Microsoft.Orleans.Clustering.AzureStorage
- Microsoft.Orleans.Clustering.AdoNet
- Microsoft.Orleans.Clustering.DynamoDB
Sono disponibili anche diversi pacchetti di terze parti per altri servizi, ad esempio CosmosDB, Kubernetes, Redis e Aerospike. Per altre informazioni, vedere Gestione del cluster in Orleans.
Progetto di esempio
Nel progetto di servizio senza stato implementare l'interfaccia ICommunicationListener
come illustrato nell'esempio seguente:
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
accetta un parametro del costruttore Func<Task<IHost>> createHost
. Viene usato successivamente per creare l'istanza di IHost
nel metodo OpenAsync
.
La parte successiva del progetto di servizio senza stato consiste nell'implementare la classe StatelessService
. Nell'esempio seguente viene illustrata la sottoclasse della 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));
}
}
Nell'esempio precedente la classe OrleansHostedStatelessService
è responsabile della resa di un'istanza di ICommunicationListener
. Il metodo CreateServiceInstanceListeners
viene chiamato dal runtime di Service Fabric quando il servizio viene inizializzato.
Eseguendo il pull di queste due classi, nell'esempio seguente viene illustrato il file Program.cs del progetto di servizio senza stato completo:
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();
}
Nel codice precedente:
- Il metodo ServiceRuntime.RegisterServiceAsync registra la classe
OrleansHostedStatelessService
con il runtime di Service Fabric. - Il delegato
CreateHostAsync
viene usato per creare l'istanza diIHost
.