Orleans guias de migração

Este artigo fornece diretrizes de migração para atualização entre as versões principais Orleans . Escolha sua versão de destino usando o seletor de versão acima.

Migrar de Orleans 7.0 para 10.0

Orleans A versão 10.0 apresenta vários novos recursos, incluindo o Painel interno. Esta seção aborda as alterações necessárias para migrar da Orleans 7.0 para a 10.0, incluindo etapas intermediárias até 8.0 e 9.0.

Resumo das alterações significativas

Alteração da falha Impacto Migração
AddGrainCallFilter removido Erro de compilação Utilize AddIncomingGrainCallFilter
LeaseAquisitionPeriod erro de digitação corrigido Erro de compilação Utilize LeaseAcquisitionPeriod
LoadSheddingLimit Renomeado Erro de compilação Utilize CpuThreshold
CancelRequestOnTimeout padrão alterado Comportamental Configure explicitamente para true, se necessário.
O provedor ADO.NET requer Microsoft.Data.SqlClient Erro de compilação/runtime Substituir o pacote System.Data.SqlClient
[Unordered] atributo obsoleto Aviso Remover atributo (não tem efeito)
OrleansConstructorAttribute Obsoleto Aviso Usar GeneratedActivatorConstructorAttribute ou ActivatorUtilitiesConstructorAttribute somente para construtores que exigem injeção de dependência, não para propriedades de dados serializadas
RegisterTimer Obsoleto Aviso Utilize RegisterGrainTimer

Atualizações de pacote

Atualize suas referências de pacote NuGet de Orleans 7.x para 10.0:

Orleans Pacote 7.x Orleans Pacote 10.0
Microsoft.Orleans.Server 7.x Microsoft.Orleans.Server 10.0.0
Microsoft.Orleans.Client 7.x Microsoft.Orleans.Client 10.0.0
Microsoft.Orleans.Sdk 7.x Microsoft.Orleans.Sdk 10.0.0
Microsoft.Orleans.Streaming.EventHubs 7.x Microsoft.Orleans.Streaming.EventHubs 10.0.0
Microsoft.Orleans.Streaming.AzureStorage 7.x Microsoft.Orleans.Streaming.AzureStorage 10.0.0
Microsoft.Orleans.Persistence.AzureStorage 7.x Microsoft.Orleans.Persistence.AzureStorage 10.0.0
Microsoft.Orleans.Clustering.AzureStorage 7.x Microsoft.Orleans.Clustering.AzureStorage 10.0.0

Alteração significativa: AddGrainCallFilter substituída por AddIncomingGrainCallFilter

O método de extensão AddGrainCallFilter em IServiceCollection foi removido. Substitua-o por AddIncomingGrainCallFilter em ISiloBuilder ou IClientBuilder.

// Orleans 7.x (no longer works)
services.AddGrainCallFilter(new MyFilter());
services.AddGrainCallFilter<MyFilter>();

// Orleans 10.0
siloBuilder.AddIncomingGrainCallFilter(new MyFilter());
siloBuilder.AddIncomingGrainCallFilter<MyFilter>();

// Or using a delegate
siloBuilder.AddIncomingGrainCallFilter(async context =>
{
    // Before grain call
    await context.Invoke();
    // After grain call
});

Para chamadas de grains de saída de clientes, use AddOutgoingGrainCallFilter:

siloBuilder.AddOutgoingGrainCallFilter<MyOutgoingFilter>();
clientBuilder.AddOutgoingGrainCallFilter<MyOutgoingFilter>();

Alteração significativa: erro de digitação em LeaseAquisitionPeriod corrigido

A propriedade LeaseAquisitionPeriod em LeaseBasedQueueBalancerOptions com ortografia incorreta foi corrigida para LeaseAcquisitionPeriod.

// Orleans 7.x (typo)
options.LeaseAquisitionPeriod = TimeSpan.FromSeconds(30);

// Orleans 10.0 (corrected)
options.LeaseAcquisitionPeriod = TimeSpan.FromSeconds(30);

Alteração significativa: LoadSheddingLimit renomeada para CpuThreshold

A propriedade LoadSheddingLimit em LoadSheddingOptions foi renomeada para CpuThreshold para refletir melhor sua finalidade.

// Orleans 7.x
siloBuilder.Configure<LoadSheddingOptions>(options =>
{
    options.LoadSheddingEnabled = true;
    options.LoadSheddingLimit = 95; // No longer works
});

// Orleans 10.0
siloBuilder.Configure<LoadSheddingOptions>(options =>
{
    options.LoadSheddingEnabled = true;
    options.CpuThreshold = 95; // Use this instead
});

Alteração significativa: valor padrão de CancelRequestOnTimeout alterado

O valor padrão de MessagingOptions.CancelRequestOnTimeout foi alterado de true para false. Isso significa que, por padrão, Orleans não envia mais uma mensagem de cancelamento quando uma chamada de grain expira.

Se o aplicativo depender do comportamento anterior, defina explicitamente esta opção:

siloBuilder.Configure<SiloMessagingOptions>(options =>
{
    options.CancelRequestOnTimeout = true;
});

// For clients
clientBuilder.Configure<ClientMessagingOptions>(options =>
{
    options.CancelRequestOnTimeout = true;
});

Alteração significativa: o provedor ADO.NET requer Microsoft.Data.SqlClient

Os provedores .NET ADO (clustering, persistência, lembretes) agora exigem Microsoft.Data.SqlClient em vez de System.Data.SqlClient. Atualize suas referências de projeto:

<!-- Remove -->
<PackageReference Include="System.Data.SqlClient" Version="..." />

<!-- Add -->
<PackageReference Include="Microsoft.Data.SqlClient" Version="5.2.0" />

O nome invariável também foi alterado:

// Orleans 7.x
options.Invariant = "System.Data.SqlClient";

// Orleans 10.0
options.Invariant = "Microsoft.Data.SqlClient";

Alteração significativa: [Unordered] atributo obsoleto

O [Unordered] atributo em interfaces de grão agora está obsoleto e não tem efeito. A ordenação de mensagens nunca foi garantida, independentemente desse atributo. Remova o atributo do código:

// Orleans 7.x
[Unordered]
public interface IMyGrain : IGrainWithStringKey
{
    Task DoSomething();
}

// Orleans 10.0 - just remove the attribute
public interface IMyGrain : IGrainWithStringKey
{
    Task DoSomething();
}

Alteração significativa: OrleansConstructorAttribute obsoleto

O OrleansConstructorAttribute foi obsoleto. Em vez disso, use GeneratedActivatorConstructorAttribute ou ActivatorUtilitiesConstructorAttribute. Aplique esses atributos apenas a construtores que exigem a injeção de serviços por meio de injeção de dependência. Não as use para indicar como Orleans deve definir propriedades ou campos de dados serializados.

public interface IMyDependency
{
}

// Orleans 7.x
[GenerateSerializer]
public class MyClass
{
    [Id(0)]
    public string Value { get; set; }

    [OrleansConstructor] // Obsolete and ignored
    public MyClass(IMyDependency dependency)
    {
        Dependency = dependency;
    }

    [field: NonSerialized]
    public IMyDependency Dependency { get; }
}

// Orleans 10.0
[GenerateSerializer]
public class MyClass
{
    [Id(0)]
    public string Value { get; set; }

    [GeneratedActivatorConstructor]
    public MyClass(IMyDependency dependency)
    {
        Dependency = dependency;
    }

    [field: NonSerialized]
    public IMyDependency Dependency { get; }
}

Alteração significativa: RegisterTimer obsoleto

O Grain.RegisterTimer método é obsoleto. Em vez disso, use os novos RegisterGrainTimer métodos de extensão, que fornecem melhor controle sobre o comportamento do temporizador.

// Orleans 7.x
public override Task OnActivateAsync(CancellationToken cancellationToken)
{
    RegisterTimer(
        callback: DoWork,
        state: null,
        dueTime: TimeSpan.FromSeconds(1),
        period: TimeSpan.FromSeconds(10));
    return Task.CompletedTask;
}

// Orleans 10.0
public override Task OnActivateAsync(CancellationToken cancellationToken)
{
    this.RegisterGrainTimer(
        callback: DoWork,
        state: (object?)null,
        options: new GrainTimerCreationOptions
        {
            DueTime = TimeSpan.FromSeconds(1),
            Period = TimeSpan.FromSeconds(10),
            Interleave = true // Set to true for same behavior as old RegisterTimer
        });
    return Task.CompletedTask;
}

Importante

Por padrão, RegisterGrainTimer usa Interleave = false, o que impede que os retornos de chamada do temporizador se intercalem com outras chamadas de grains. Se você precisar do comportamento antigo, em que os retornos de chamada do temporizador podiam se intercalar, defina Interleave = true explicitamente.

Novos recursos na Orleans versão 10.0

Após a migração, você pode aproveitar esses novos recursos:

Migração de Orleans 8.0 para 9.0

Se você estiver atualizando do Orleans 8.x, observe estas alterações adicionais introduzidas na Orleans versão 9.0:

  • Diretório de grãos de consistência forte: o diretório de grãos padrão agora fornece garantias de consistência mais fortes
  • Suporte total ao CancellationToken: os métodos Grain agora dão suporte total aos parâmetros CancellationToken
  • Desativação de ativação baseada em memória: desativação automática de grains sob pressão de memória
  • Protocolo de associação mais rápido: tempo de detecção de falha padrão reduzido de 10 minutos para 90 segundos
  • Posicionamento padrão alterado para ResourceOptimized (9.2+): a estratégia de posicionamento de grão padrão mudou de RandomPlacement para ResourceOptimizedPlacement

Se o aplicativo depender do posicionamento aleatório, configure-o explicitamente:

siloBuilder.Services.AddSingleton<PlacementStrategy, RandomPlacement>();

// Or on specific grains
[RandomPlacement]
public class MyGrain : Grain, IMyGrain { }

Migração de Orleans 7.0 para 8.0

Se você estiver atualizando do Orleans 7.x, observe estas alterações introduzidas na Orleans 8.0:

  • Nova API do Temporizador: RegisterGrainTimer foi introduzida para substituir RegisterTimer
  • .NET Aspire integration: suporte de primeira classe para .NET Aspire
  • Posicionamento otimizado para recursos: nova estratégia de posicionamento baseada na utilização de CPU e memória
  • Reparticionamento de ativação (8.2+): recurso experimental para reequilíbrio automático de grãos

Atualizações sem interrupção

Atualizações sem interrupção de Orleans 7.x para 10.0 não são recomendadas devido às alterações significativas de protocolo e de API. Em seu lugar:

  1. Implantar um novo cluster executando Orleans 10.0
  2. Migrar dados de estado, se necessário
  3. Alternar o tráfego para o novo cluster
  4. Desativar o cluster antigo

Scripts de migração do ADO.NET

Se você usar o ADO.NET para clustering, persistência ou lembretes, aplique os scripts de migração apropriados:

Migrar do Orleans 3.x para o 7.0

O Orleans 7.0 apresenta várias alterações benéficas, incluindo melhorias na hospedagem, serialização personalizada, imutabilidade e abstrações de granularidade.

Migração

Devido a alterações na forma como Orleans identifica grãos e fluxos, a migração de aplicativos existentes usando lembretes, fluxos ou persistência de grãos para Orleans 7.0 no momento não é simples.

Não é possível atualizar sem problemas os aplicativos que executam versões anteriores Orleans via uma atualização gradual para Orleans 7.0. Portanto, use uma estratégia de atualização diferente, como implantar um novo cluster e desativar o anterior. Orleans A versão 7.0 altera o protocolo de comunicação de forma incompatível, o que significa que os clusters não podem conter uma mistura de Orleans hosts 7.0 e hosts executando versões Orleans.

Essas alterações que quebram a compatibilidade foram evitadas por muitos anos, mesmo entre versões principais. Por que agora? Há dois motivos principais: identidades e serialização. Em relação às identidades, as identidades de grains e fluxos agora consistem em strings. Isso permite que os grãos codificam informações de tipo genérico corretamente e facilita o mapeamento de fluxos para o domínio do aplicativo. Anteriormente, Orleans identificava tipos de grãos usando uma complexa estrutura de dados que não podia representar grãos genéricos, levando a casos extremos. Os fluxos foram identificados por um string namespace e uma Guid chave, que foi eficiente, mas difícil de mapear para o domínio do aplicativo. A serialização agora é tolerante a versões. Isso significa que os tipos podem ser modificados de determinadas maneiras compatíveis, seguindo um conjunto de regras, com confiança de que o aplicativo pode ser atualizado sem erros de serialização. Essa capacidade é especialmente útil quando os tipos de aplicativos persistem em fluxos ou armazenamento de grains. As seções a seguir detalham as principais alterações e as discutem ainda mais.

Alterações de embalagem

Ao atualizar um projeto para Orleans 7.0, execute as seguintes ações:

  • Todos os clientes devem referenciar Microsoft.Orleans. Cliente.
  • Todos os silos (servidores) devem referenciar Microsoft.Orleans.Server.
  • Todos os outros pacotes devem referenciar Microsoft.Orleans. Sdk.
  • Remova todas as referências a Microsoft.Orleans.CodeGenerator.MSBuild e Microsoft.Orleans.OrleansCodeGenerator.Build.
  • Remova todas as referências a Microsoft.Orleans.OrleansRuntime.
  • Remova chamadas para ConfigureApplicationParts. As Partes do Aplicativo foram removidas. O C# Source Generator para Orleans é adicionado a todos os pacotes (incluindo o cliente e o servidor) e gera automaticamente o equivalente a Partes da Aplicação.
  • Substitua referências a Microsoft.Orleans.OrleansServiceBus por Microsoft.Orleans. Streaming.EventHubs.
  • Se estiver usando lembretes, adicione uma referência a Microsoft.Orleans.Reminders.
  • Se estiver usando fluxos, adicione uma referência a Microsoft.Orleans. Streaming.

Dica

Todos os exemplos do Orleans foram atualizados para o Orleans 7.0 e podem ser usados como referência para as alterações que forem feitas. Para mais informações, consulte o tópico Orleans #8035, que lista as alterações feitas a cada amostra.

Orleans diretivas de uso global

Todos os projetos Orleans referenciam direta ou indiretamente o pacote NuGet Microsoft.Orleans.Sdk. Quando um Orleans projeto é configurado para habilitar usos implícitos (por exemplo, <ImplicitUsings>enable</ImplicitUsings>), o projeto usa implicitamente os namespaces Orleans e Orleans.Hosting. Isso significa que o código do aplicativo não precisa dessas using diretivas.

Para obter mais informações, consulte ImplicitUsings e dotnet/orleans/src/Orleans. Sdk/build/Microsoft.Orleans. Sdk.targets.

Hosting

O tipo ClientBuilder é substituído pelo método de extensão UseOrleansClient em IHostBuilder. O tipo IHostBuilder vem do Microsoft. Extensions.Hosting pacote NuGet. Isso significa que um Orleans cliente pode ser adicionado a um host existente sem criar um contêiner de injeção de dependência separado. O cliente se conecta ao cluster durante a inicialização. Assim que IHost.StartAsync for concluído, o cliente se conecta automaticamente. Os serviços adicionados ao IHostBuilder começam na ordem de registro. Chamar UseOrleansClient antes de chamar ConfigureWebHostDefaults, por exemplo, garante que Orleans seja iniciado antes do início do ASP.NET Core, permitindo o acesso imediato ao cliente do aplicativo ASP.NET Core.

Para emular o comportamento anterior ClientBuilder , crie um separado HostBuilder e configure-o com um Orleans cliente. Um IHostBuilder pode ser configurado com um cliente Orleans ou um silo Orleans. Todos os silos registram uma instância de IGrainFactory e IClusterClient que o aplicativo pode usar, portanto, configurar um cliente separadamente é desnecessário e sem suporte.

Alteração de assinatura de OnActivateAsync e OnDeactivateAsync

O Orleans permite que os grãos executem o código durante a ativação e a desativação. Use esse recurso para executar tarefas como ler o estado do armazenamento ou registrar mensagens de ciclo de vida em log. No Orleans 7.0, a assinatura desses métodos de ciclo de vida mudou:

Considere o seguinte exemplo de uma granularidade substituindo esses novos métodos:

public sealed class PingGrain : Grain, IPingGrain
{
    private readonly ILogger<PingGrain> _logger;

    public PingGrain(ILogger<PingGrain> logger) =>
        _logger = logger;

    public override Task OnActivateAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("OnActivateAsync()");
        return Task.CompletedTask;
    }

    public override Task OnDeactivateAsync(DeactivationReason reason, CancellationToken token)
    {
        _logger.LogInformation("OnDeactivateAsync({Reason})", reason);
        return Task.CompletedTask;
    }

    public ValueTask Ping() => ValueTask.CompletedTask;
}

Grãos POCO e IGrainBase

As granularidades no Orleans não precisam mais herdar da classe base Grain nem de qualquer outra classe. Essa funcionalidade é conhecida como granularidades POCO. Para acessar métodos de extensão, tais como qualquer um dos seguintes:

O grain deve implementar IGrainBase ou herdar de Grain. Aqui está um exemplo de implementação de IGrainBase em uma classe grain:

public sealed class PingGrain : IGrainBase, IPingGrain
{
    public PingGrain(IGrainContext context) => GrainContext = context;

    public IGrainContext GrainContext { get; }

    public ValueTask Ping() => ValueTask.CompletedTask;
}

IGrainBase também define OnActivateAsync e OnDeactivateAsync com implementações padrão, permitindo que os grãos participem de seu ciclo de vida, se desejado:

public sealed class PingGrain : IGrainBase, IPingGrain
{
    private readonly ILogger<PingGrain> _logger;

    public PingGrain(IGrainContext context, ILogger<PingGrain> logger)
    {
        _logger = logger;
        GrainContext = context;
    }

    public IGrainContext GrainContext { get; }

    public Task OnActivateAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("OnActivateAsync()");
        return Task.CompletedTask;
    }

    public Task OnDeactivateAsync(DeactivationReason reason, CancellationToken token)
    {
        _logger.LogInformation("OnDeactivateAsync({Reason})", reason);
        return Task.CompletedTask;
    }

    public ValueTask Ping() => ValueTask.CompletedTask;
}

Serialização

A mudança mais pesada no Orleans 7.0 é a introdução do serializador tolerante a versão. Essa alteração foi feita porque os aplicativos tendem a evoluir, o que levou a uma armadilha significativa para os desenvolvedores, uma vez que o serializador anterior não podia tolerar a adição de propriedades aos tipos existentes. Por outro lado, o serializador anterior era flexível, permitindo a representação da maioria dos tipos de .NET sem modificação, incluindo recursos como genéricos, polimorfismo e acompanhamento de referência. Uma substituição estava há muito atrasada, mas a representação de alta fidelidade dos tipos ainda é necessária. Portanto, Orleans 7.0 introduz um serializador de substituição que dá suporte à representação de alta fidelidade de tipos de .NET e, ao mesmo tempo, permite que os tipos evoluam. O novo serializador é muito mais eficiente que o anterior, resultando em um aumento de até 170% na taxa de transferência de ponta a ponta.

Para obter mais informações, confira os seguintes artigos relacionados ao Orleans 7.0:

Identidades de granularidade

Cada um dos grãos tem uma identidade exclusiva composta pelo tipo do grão e sua chave. Versões anteriores de Orleans usavam um tipo composto para GrainIds para suportar chaves de grain de:

Essa abordagem envolve alguma complexidade ao lidar com chaves de grain. As identidades de granularidade consistem em dois componentes: um tipo e uma chave. O componente de tipo consistia anteriormente em um código de tipo numérico, uma categoria e 3 bytes de informações de tipo genérico.

As identidades de grãos agora assumem a forma type/key, onde tanto type quanto key são cadeias de caracteres. A interface de chave de grain mais comumente usada é IGrainWithStringKey. Isso simplifica muito o funcionamento da identidade de granularidade e melhora o suporte para tipos genéricos de granularidade.

As interfaces grain agora também são representadas usando um nome fácil de ler, em vez de uma combinação de um código hash e uma representação textual de qualquer parâmetro de tipo genérico.

O novo sistema é mais personalizável e essas personalizações podem ser controladas com atributos.

  • GrainTypeAttribute(String) em um grain class especifica a porção Tipo de seu ID de grain.
  • DefaultGrainTypeAttribute(String) em um grain interface especifica o Tipo do grain que IGrainFactory deve resolver por padrão ao obter uma referência de grain. Por exemplo, ao chamar IGrainFactory.GetGrain<IMyGrain>("my-key"), a fábrica de grãos retornará uma referência ao grão "my-type/my-key" se IMyGrain tiver o atributo mencionado acima especificado.
  • GrainInterfaceTypeAttribute(String) permite substituir o nome da interface. Especificar um nome explicitamente usando esse mecanismo permite renomear o tipo de interface sem interromper a compatibilidade com referências de grãos existentes. Observe que a interface também deve ter o AliasAttribute no caso, pois sua identidade pode ser serializada. Para mais informações sobre como especificar um alias de tipo, consulte a seção sobre serialização.

Como mencionado acima, sobrescrever a classe de grain padrão e os nomes de interface para tipos permite renomear os tipos subjacentes sem quebrar a compatibilidade com implantações existentes.

Identidades de fluxo

Quando os fluxos de Orleans foram lançados pela primeira vez, eles só podiam ser identificados usando um Guid. Essa abordagem foi eficiente em termos de alocação de memória, mas dificultou a criação de identidades de fluxo significativas, muitas vezes exigindo alguma codificação ou indireção para determinar a identidade de fluxo apropriada para uma determinada finalidade.

Na Orleans versão 7.0, os fluxos são identificados usando cadeias de caracteres. O Orleans.Runtime.StreamIdstruct contém três propriedades: StreamId.Namespace, StreamId.Keye StreamId.FullKey. Esses valores de propriedade são cadeias de caracteres UTF-8 codificadas. Por exemplo, veja StreamId.Create(String, String).

Substituição de SimpleMessageStreams por BroadcastChannel

SimpleMessageStreams (também chamado de SMS) é removido na versão 7.0. O SMS tinha a mesma interface que Orleans.Providers.Streams.PersistentStreams, mas seu comportamento era muito diferente porque dependia de chamadas diretas de grain para grain. Para evitar confusão, o SMS foi removido e uma nova substituição chamada Orleans.BroadcastChannel foi introduzida.

BroadcastChannel só dá suporte a assinaturas implícitas e pode ser um substituto direto nesse caso. Se assinaturas explícitas forem necessárias ou a PersistentStream interface precisar ser usada (por exemplo, se o SMS foi usado em testes enquanto EventHub foi usado na produção), então MemoryStream é o melhor candidato.

BroadcastChannel tem os mesmos comportamentos que o SMS, enquanto MemoryStream se comporta como outros provedores de fluxo. Considere o seguinte exemplo de uso do Broadcast Channel:

// Configuration
builder.AddBroadcastChannel(
    "my-provider",
    options => options.FireAndForgetDelivery = false);

// Publishing
var grainKey = Guid.NewGuid().ToString("N");
var channelId = ChannelId.Create("some-namespace", grainKey);
var stream = provider.GetChannelWriter<int>(channelId);

await stream.Publish(1);
await stream.Publish(2);
await stream.Publish(3);

// Simple implicit subscriber example
[ImplicitChannelSubscription]
public sealed class SimpleSubscriberGrain : Grain, ISubscriberGrain, IOnBroadcastChannelSubscribed
{
    // Called when a subscription is added to the grain
    public Task OnSubscribed(IBroadcastChannelSubscription streamSubscription)
    {
        streamSubscription.Attach<int>(
          item => OnPublished(streamSubscription.ChannelId, item),
          ex => OnError(streamSubscription.ChannelId, ex));

        return Task.CompletedTask;

        // Called when an item is published to the channel
        static Task OnPublished(ChannelId id, int item)
        {
            // Do something
            return Task.CompletedTask;
        }

        // Called when an error occurs
        static Task OnError(ChannelId id, Exception ex)
        {
            // Do something
            return Task.CompletedTask;
        }
    }
}

A migração para MemoryStream é mais fácil, pois só é necessário alterar a configuração. Considere a configuração MemoryStream a seguir:

builder.AddMemoryStreams<DefaultMemoryMessageBodySerializer>(
    "in-mem-provider",
    _ =>
    {
        // Number of pulling agent to start.
        // DO NOT CHANGE this value once deployed, if you do rolling deployment
        _.ConfigurePartitioning(partitionCount: 8);
    });

OpenTelemetry

O sistema de telemetria é atualizado em Orleans 7.0 e o sistema anterior é removido em favor de APIs de .NET padronizadas, como Métricas de .NET para métricas e ActivitySource para rastreamento.

Como parte disso, os pacotes Microsoft.Orleans.TelemetryConsumers.* existentes são removidos. Um novo conjunto de pacotes está sendo considerado para simplificar a integração de métricas emitidas pela Orleans solução de monitoramento escolhida. Como sempre, comentários e contribuições são bem-vindos.

A ferramenta dotnet-counters oferece monitoramento de desempenho para monitoramento de integridade ad hoc e análise de desempenho de nível inicial. Para contadores Orleans, use a ferramenta dotnet-counters para monitorá-los:

dotnet counters monitor -n MyApp --counters Microsoft.Orleans

Da mesma forma, adicione os medidores Microsoft.Orleans às métricas do OpenTelemetry, conforme mostrado no código a seguir:

builder.Services.AddOpenTelemetry()
    .WithMetrics(metrics => metrics
        .AddPrometheusExporter()
        .AddMeter("Microsoft.Orleans"));

Para habilitar o rastreamento distribuído, configure OpenTelemetry, conforme mostrado no seguinte código:

builder.Services.AddOpenTelemetry()
    .WithTracing(tracing =>
    {
        tracing.SetResourceBuilder(ResourceBuilder.CreateDefault()
            .AddService(serviceName: "ExampleService", serviceVersion: "1.0"));

        tracing.AddAspNetCoreInstrumentation();
        tracing.AddSource("Microsoft.Orleans.Runtime");
        tracing.AddSource("Microsoft.Orleans.Application");

        tracing.AddZipkinExporter(options =>
        {
            options.Endpoint = new Uri("http://localhost:9411/api/v2/spans");
        });
    });

No código anterior, OpenTelemetry está configurado para monitorar:

  • Microsoft.Orleans.Runtime
  • Microsoft.Orleans.Application

Para propagar a atividade, chame AddActivityPropagation:

builder.Host.UseOrleans((_, clientBuilder) =>
{
    clientBuilder.AddActivityPropagation();
});

Refatorar recursos do pacote principal em pacotes separados

Na versão Orleans 7.0, as extensões foram incorporadas em pacotes separados que não dependem de Orleans.Core. Ou seja, Orleans.Streaminge Orleans.RemindersOrleans.Transactions foram separados do núcleo. Isso significa que esses pacotes são totalmente pagos pelo que são usados, e nenhum código no núcleo Orleans é dedicado a esses recursos. Essa abordagem reduz a superfície da API principal e o tamanho do assembly, simplifica o núcleo e melhora o desempenho. Em relação ao desempenho, as transações em Orleans anteriormente exigiam a execução de algum código para cada método, a fim de coordenar possíveis transações. Essa lógica de coordenação agora foi movida para uma base por método.

Esta é uma alteração que quebra a compilação. O código existente que interage com lembretes ou fluxos chamando métodos previamente definidos na classe base Grain pode quebrar, pois esses agora são métodos de extensão. Atualize essas chamadas que não especificam this (por exemplo, GetReminders) para incluir this (por exemplo, this.GetReminders()) porque os métodos de extensão devem ser qualificados. Ocorrerá um erro de compilação se essas chamadas não forem atualizadas e a alteração de código necessária não for óbvia sem saber o que foi alterado.

Cliente de transação

Orleans 7.0 apresenta uma nova abstração para coordenar transações: Orleans.ITransactionClient. Anteriormente, somente os grãos podiam coordenar transações. Com ITransactionClient, disponível via injeção de dependência, os clientes também podem coordenar transações sem a necessidade de um grain intermediário. O exemplo a seguir retira créditos de uma conta e os deposita em outra dentro de uma única transação. Chame este código de dentro de um grain ou de um cliente externo que recuperou o ITransactionClient do contêiner de injeção de dependência.

await transactionClient.RunTransaction(
  TransactionOption.Create,
  () => Task.WhenAll(from.Withdraw(100), to.Deposit(100)));

Para transações coordenadas pelo cliente, o cliente deve adicionar os serviços necessários durante a configuração:

clientBuilder.UseTransactions();

O exemplo BankAccount demonstra o uso de ITransactionClient. Para obter mais informações, consulte Orleans transações.

Reentrância da cadeia de chamadas

As granularidades são de thread único e processam solicitações uma a uma do início à conclusão por padrão. Em outras palavras, as granularidades não são reentrantes por padrão. Adicionar o ReentrantAttribute a uma classe de grain permite que o grain processe várias solicitações simultaneamente de forma intercalada, mantendo-se em thread única. Essa funcionalidade pode ser útil para grãos que não contêm nenhum estado interno ou executam muitas operações assíncronas, como emitir chamadas HTTP ou gravar em um banco de dados. É necessário cuidado extra quando as solicitações podem ser intercaladas: é possível que o estado de um grain observado antes de uma instrução await mude até o momento em que a operação assíncrona for concluída e o método retomar a execução.

Por exemplo, o grão a seguir representa um contador. Está marcado como ReentrantAttribute, permitindo que várias chamadas sejam intercaladas. O método Increment() deve incrementar o contador interno e retornar o valor observado. No entanto, como o corpo do método Increment() observa o estado do grain antes de um ponto await e o atualiza posteriormente, múltiplas execuções intercaladas de Increment() podem resultar em um _value menor que o número total de chamadas Increment() recebidas. Esse é um erro introduzido pelo uso inadequado da reentrância.

Remover o ReentrantAttribute é suficiente para corrigir esse problema.

[Reentrant]
public sealed class CounterGrain : Grain, ICounterGrain
{
    int _value;

    /// <summary>
    /// Increments the grain's value and returns the previous value.
    /// </summary>
    public Task<int> Increment()
    {
        // Do not copy this code, it contains an error.
        var currentVal = _value;
        await Task.Delay(TimeSpan.FromMilliseconds(1_000));
        _value = currentVal + 1;
        return currentValue;
    }
}

Para evitar esses erros, as granularidades não são reentrantes por padrão. A desvantagem é a redução da taxa de transferência para grains que executam operações assíncronas em sua implementação, uma vez que o grain não pode processar outras solicitações enquanto aguarda a conclusão de uma operação assíncrona. Para aliviar isso, o Orleans oferece várias opções para permitir a reentrância em determinados casos:

  • Para uma classe inteira: colocar o ReentrantAttribute no grain permite que qualquer solicitação ao grain se intercale com qualquer outra solicitação.
  • Para um subconjunto de métodos: colocar o AlwaysInterleaveAttribute no método de interface do grain permite que as solicitações a esse método se intercalem com qualquer outra solicitação e permite que qualquer outra solicitação intercale solicitações a esse método.
  • Para um subconjunto de métodos: colocar o ReadOnlyAttribute no método de interface do grain permite que as solicitações para esse método se intercalem com qualquer outra solicitação ReadOnlyAttribute e permite que qualquer outra solicitação ReadOnlyAttribute intercale solicitações para esse método. Nesse sentido, é uma forma mais restrita de AlwaysInterleaveAttribute.
  • Para qualquer solicitação dentro de uma cadeia de chamadas: RequestContext.AllowCallChainReentrancy() e RequestContext.SuppressCallChainReentrancy() permitem optar por permitir ou não que solicitações subsequentes reentrem no grain. Ambas as chamadas retornam um valor que deve ser descartado ao sair da solicitação. Portanto, use-os da seguinte maneira:
public Task<int> OuterCall(IMyGrain other)
{
    // Allow call-chain reentrancy for this grain, for the duration of the method.
    using var _ = RequestContext.AllowCallChainReentrancy();
    await other.CallMeBack(this.AsReference<IMyGrain>());
}

public Task CallMeBack(IMyGrain grain)
{
    // Because OuterCall allowed reentrancy back into that grain, this method
    // will be able to call grain.InnerCall() without deadlocking.
    await grain.InnerCall();
}

public Task InnerCall() => Task.CompletedTask;

Opção de reentrância da cadeia de chamadas por grain, por cadeia de chamadas. Por exemplo, considere dois grains, A e B. Se o grain A habilitar a reentrância da cadeia de chamadas antes de chamar o grain B, o grain B poderá chamar o grain A de volta nessa chamada. No entanto, o grain A não pode chamar de volta o grain B se o grain B não tiver também habilitado a reentrância da cadeia de chamadas. É habilitado por grain, por cadeia de chamadas.

As granularidades também podem suprimir as informações de reentrância da cadeia de chamadas de fluir para baixo em uma cadeia de chamadas usando using var _ = RequestContext.SuppressCallChainReentrancy(). Isso impede que chamadas subsequentes sejam reiniciadas.

Scripts de migração do ADO.NET

Para garantir a compatibilidade futura com Orleans clustering, persistência e lembretes que dependem do ADO.NET, é necessário o script de migração SQL apropriado.

Selecione os arquivos para o banco de dados usado e aplique-os na ordem.

Migrar do Orleans 3.x para o 7.0

Para usuários da versão 3.x, siga as diretrizes de migração na seção de documentação 7.0 usando o seletor de versão acima.

Importante

Orleans Não há mais suporte para 3.x. Considere a migração para Orleans 10.0 para os recursos e atualizações de segurança mais recentes.