Reliable Collection object serialization in Azure Service Fabric

As Coleções Fiáveis replicam e mantêm os itens para garantir que são duráveis em todas as falhas de energia e falhas de energia da máquina. Tanto para replicar como para manter os itens, as Coleções Fiáveis precisam de os serializar.

O Reliable Collections obtém o serializador adequado para um determinado tipo a partir do Reliable State Manager. O Reliable State Manager contém serializadores incorporados e permite que os serializadores personalizados sejam registados para um determinado tipo.

Serializadores Incorporados

O Reliable State Manager inclui serializador incorporado para alguns tipos comuns, para que possam ser serializados de forma eficiente por predefinição. Para outros tipos, o Reliable State Manager volta a utilizar o DataContractSerializer. Os serializadores incorporados são mais eficientes, uma vez que sabem que os seus tipos não podem ser alterados e não precisam de incluir informações sobre o tipo como o nome do tipo.

O Reliable State Manager tem um serializador incorporado para os seguintes tipos:

  • GUID
  • bool
  • byte
  • sbyte
  • byte[]
  • caráter
  • string
  • decimal
  • double
  • float
  • int
  • uint
  • long
  • ulong
  • abreviatura
  • ushort

Serialização Personalizada

Os serializadores personalizados são normalmente utilizados para aumentar o desempenho ou para encriptar os dados através da transmissão e no disco. Entre outras razões, os serializadores personalizados são normalmente mais eficientes do que o serializador genérico, uma vez que não precisam de serializar informações sobre o tipo.

IReliableStateManager.TryAddStateSerializer< O T> é utilizado para registar um serializador personalizado para o tipo T especificado. Este registo deve ocorrer na construção do StatefulServiceBase para garantir que, antes do início da recuperação, todas as Coleções Fiáveis têm acesso ao serializador relevante para ler os respetivos 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 serializadores incorporados. Por exemplo, quando um serializador personalizado para int é registado, é utilizado para serializar números inteiros em vez do serializador incorporado para int.

Como implementar um serializador personalizado

Um serializador personalizado tem de implementar a interface T> IStateSerializer<.

Nota

O IStateSerializer<T> inclui uma sobrecarga para Escrita e Leitura que recebe um T adicional chamado valor base. Esta API destina-se à serialização diferencial. Atualmente, a funcionalidade de serialização diferencial não está exposta. Por conseguinte, estas duas sobrecargas não são chamadas até que a serialização diferencial seja exposta e ativada.

Segue-se um tipo personalizado de exemplo 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
}

Segue-se um exemplo de implementação de IStateSerializer<OrderKey>. Tenha em atenção que as sobrecargas de Leitura e Escrita que assumem baseValue, chamam a respetiva sobrecarga para compatibilidade de reencaminhamentos.

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

Atualização

Numa atualização sem interrupção da aplicação, a atualização é aplicada a um subconjunto de nós, um domínio de atualização de cada vez. Durante este processo, alguns domínios de atualização estarão na versão mais recente da sua aplicação e alguns domínios de atualização estarão na versão mais antiga da sua aplicação. Durante a implementação, a nova versão da sua aplicação tem de conseguir ler a versão antiga dos seus dados e a versão antiga da sua aplicação tem de conseguir ler a nova versão dos seus dados. Se o formato de dados não for reencaminhado e retrocompatível, a atualização pode falhar ou, pior ainda, os dados podem ser perdidos ou danificados.

Se estiver a utilizar um serializador incorporado, não tem de se preocupar com a compatibilidade. No entanto, se estiver a utilizar um serializador personalizado ou o DataContractSerializer, os dados têm de ser infinitamente retroceder e reencaminhar compatíveis. Por outras palavras, cada versão do serializador tem de ser capaz de serializar e des serializar qualquer versão do tipo.

Os utilizadores do Contrato de Dados devem seguir as regras de controlo de versões bem definidas para adicionar, remover e alterar campos. O Contrato de Dados também tem suporte para lidar com campos desconhecidos, ligar-se ao processo de serialização e desserialização e lidar com a herança de classes. Para obter mais informações, veja Utilizar o Contrato de Dados.

Os utilizadores serializadores personalizados devem seguir as diretrizes do serializador que estão a utilizar para garantir que é compatível com versões anteriores e reencaminhamentos. Uma forma 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.

Passos seguintes