Partilhar via


Serialização de objeto de coleção confiável no Azure Service Fabric

As Coleções Confiáveis replicam e persistem seus itens para garantir que eles sejam duráveis em caso de falhas de máquinas e quedas de energia. Tanto para replicar quanto para persistir itens, as Coleções Confiáveis precisam serializá-los.

Coleções confiáveis obtêm o serializador apropriado para um determinado tipo do Reliable State Manager. O Reliable State Manager contém serializadores internos e permite que serializadores personalizados sejam registrados para um determinado tipo.

Serializadores integrados

O Reliable State Manager inclui serializador interno para alguns tipos comuns, para que eles possam ser serializados de forma eficiente por padrão. Para outros tipos, o Reliable State Manager volta a usar o DataContractSerializer. Os serializadores internos são mais eficientes, pois sabem que seus tipos não podem ser alterados e não precisam incluir informações sobre o tipo, como seu nome de tipo.

O Reliable State Manager tem serializador integrado para os seguintes tipos:

  • GUID
  • booleano
  • byte
  • Sbyte
  • byte[]
  • char
  • string
  • decimal
  • duplo
  • flutuante
  • número inteiro
  • uint
  • long
  • Ulong
  • curtas
  • Ushort

Serialização personalizada

Os serializadores personalizados são comumente usados para aumentar o desempenho ou para criptografar os dados por fio e no disco. Entre outras razões, os serializadores personalizados são geralmente mais eficientes do que os serializadores genéricos, uma vez que não precisam serializar informações sobre o tipo.

IReliableStateManager.TryAddStateSerializer<T> é usado para registrar um serializador personalizado para o tipo T. Esse registro deve acontecer na construção do StatefulServiceBase para garantir que, antes do início da recuperação, todas as coleções confiáveis tenham acesso ao serializador relevante para ler seus dados persistentes.

public StatefulBackendService(StatefulServiceContext context)
  : base(context)
  {
    if (!this.StateManager.TryAddStateSerializer(new OrderKeySerializer()))
    {
      throw new InvalidOperationException("Failed to set OrderKey custom serializer");
    }
  }

Nota

Os serializadores personalizados têm precedência sobre os serializadores internos. Por exemplo, quando um serializador personalizado para int é registrado, ele é usado para serializar inteiros em vez do serializador interno para int.

Como implementar um serializador personalizado

Um serializador personalizado precisa implementar a interface T IStateSerializer<.>

Nota

IStateSerializer<T> inclui uma sobrecarga para Write and Read que recebe um T adicional chamado valor base. Esta API é para serialização diferencial. Atualmente, o recurso de serialização diferencial não está exposto. Assim, essas duas sobrecargas não são chamadas até que a serialização diferencial seja exposta e habilitada.

A seguir está um exemplo de tipo personalizado chamado OrderKey que contém quatro propriedades

public class OrderKey : IComparable<OrderKey>, IEquatable<OrderKey>
{
    public byte Warehouse { get; set; }

    public short District { get; set; }

    public int Customer { get; set; }

    public long Order { get; set; }

    #region Object Overrides for GetHashCode, CompareTo and Equals
    #endregion
}

A seguir está um exemplo de implementação de IStateSerializer<OrderKey>. Observe que as sobrecargas de leitura e gravação que recebem baseValue chamam suas respetivas sobrecargas para compatibilidade de encaminhamentos.

public class OrderKeySerializer : IStateSerializer<OrderKey>
{
  OrderKey IStateSerializer<OrderKey>.Read(BinaryReader reader)
  {
      var value = new OrderKey();
      value.Warehouse = reader.ReadByte();
      value.District = reader.ReadInt16();
      value.Customer = reader.ReadInt32();
      value.Order = reader.ReadInt64();

      return value;
  }

  void IStateSerializer<OrderKey>.Write(OrderKey value, BinaryWriter writer)
  {
      writer.Write(value.Warehouse);
      writer.Write(value.District);
      writer.Write(value.Customer);
      writer.Write(value.Order);
  }
  
  // Read overload for differential de-serialization
  OrderKey IStateSerializer<OrderKey>.Read(OrderKey baseValue, BinaryReader reader)
  {
      return ((IStateSerializer<OrderKey>)this).Read(reader);
  }

  // Write overload for differential serialization
  void IStateSerializer<OrderKey>.Write(OrderKey baseValue, OrderKey newValue, BinaryWriter writer)
  {
      ((IStateSerializer<OrderKey>)this).Write(newValue, writer);
  }
}

Capacidade de atualização

Em uma atualização de aplicativo sem interrupção, a atualização é aplicada a um subconjunto de nós, um domínio de atualização de cada vez. Durante esse processo, alguns domínios de atualização estarão na versão mais recente do seu aplicativo, e alguns domínios de atualização estarão na versão mais antiga do seu aplicativo. Durante a implantação, a nova versão do aplicativo deve ser capaz de ler a versão antiga dos dados e a versão antiga do aplicativo deve ser capaz de ler a nova versão dos dados. Se o formato de dados não for compatível com versões anteriores e posteriores, a atualização pode falhar ou, pior, os dados podem ser perdidos ou corrompidos.

Se você estiver usando o serializador interno, não precisa se preocupar com a compatibilidade. No entanto, se você estiver usando um serializador personalizado ou o DataContractSerializer, os dados devem ser infinitamente compatíveis com versões anteriores e futuras. Em outras palavras, cada versão do serializador precisa ser capaz de serializar e desserializar qualquer versão do tipo.

Os usuários do Contrato de Dados devem seguir as regras de controle de versão bem definidas para adicionar, remover e alterar campos. O Data Contract também tem suporte para lidar com campos desconhecidos, conectar-se ao processo de serialização e desserialização e lidar com herança de classe. Para obter mais informações, consulte Usando contrato de dados.

Os usuários do serializador personalizado devem aderir às diretrizes do serializador que estão usando para garantir que ele seja compatível com versões anteriores e futuras. A maneira comum de suportar todas as versões é adicionar informações de tamanho no início e adicionar apenas propriedades opcionais. Desta forma, cada versão pode ler o máximo que puder e saltar sobre a parte restante do fluxo.

Próximos passos