Partilhar via


Host com Service Fabric

Orleanspode ser hospedado no Azure Service Fabric usando Microsoft.ServiceFabric.Services e Microsoft.Orleans. Pacotes NuGet do servidor. Os silos devem ser hospedados como serviços não particionados e sem estado, uma vez que Orleans gerencia a distribuição dos próprios grãos. Outras opções de hospedagem, como particionado e stateful, são mais complexas e não geram benefícios sem personalização adicional por parte do desenvolvedor. Recomenda-se hospedar Orleans não particionado e sem monitoração de estado.

Serviço sem estado do Service Fabric como um silo

Quer esteja criando um novo aplicativo do Service Fabric ou adicionando Orleans a um existente, você precisará das Microsoft.ServiceFabric.Services referências e Microsoft.Orleans.Server do pacote em seu projeto. O projeto de serviço sem estado precisa de uma implementação na ICommunicationListener e uma subclasse do StatelessService.

O ciclo de vida do Silo segue o ciclo de vida típico do ouvinte de comunicação:

Uma vez que Orleans os silos são capazes de viver dentro dos limites do IHost, a implementação do ICommunicationListener é um invólucro em torno do IHost. O IHost é inicializado no OpenAsync método e terminado graciosamente no CloseAsync método:

ICommunicationListener IHost Interações
OpenAsync A IHost instância é criada e uma chamada para StartAsync é feita.
CloseAsync Aguarda-se uma chamada para StopAsync na instância do host.
Abort Uma chamada para StopAsync é avaliada com força, com GetAwaiter().GetResult().

Suporte a clusters

O suporte oficial ao clustering está disponível a partir de vários pacotes, incluindo:

Há também vários pacotes de terceiros disponíveis para outros serviços, como CosmosDB, Kubernetes, Redis e Aerospike. Para obter mais informações, consulte Gerenciamento de cluster em Orleans.

Exemplo de projeto

No projeto de serviço sem estado, implemente a ICommunicationListener interface conforme mostrado no exemplo a seguir:

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

A HostedServiceCommunicationListener classe aceita um Func<Task<IHost>> createHost parâmetro do construtor. Isso é usado posteriormente para criar a IHost instância no OpenAsync método.

A próxima parte do projeto de serviço sem estado é implementar a StatelessService classe. O exemplo a seguir mostra a subclasse da StatelessService classe:

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

No exemplo anterior, a OrleansHostedStatelessService classe é responsável por produzir uma ICommunicationListener instância. O CreateServiceInstanceListeners método é chamado pelo tempo de execução do Service Fabric quando o serviço é inicializado.

Juntando essas duas classes, o exemplo a seguir mostra o projeto de serviço sem estado completo Program.cs arquivo:

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

No código anterior:

  • O ServiceRuntime.RegisterServiceAsync método registra a OrleansHostedStatelessService classe com o tempo de execução do Service Fabric.
  • O CreateHostAsync delegado é usado para criar a IHost instância.