Compartilhar via


Visão geral da serialização de exceção na comunicação remota

A serialização baseada em BinaryFormatter não é segura, portanto, não use o BinaryFormatter para processamento de dados. Para obter mais informações sobre as implicações de segurança, consulte Riscos de desserialização no uso do BinaryFormatter e tipos relacionados.

O Azure Service Fabric usava o BinaryFormatter para serializar exceções. A partir do ServiceFabric v9.0, a serialização baseada em contrato de dados para exceções na comunicação remota está disponibilizada como um recurso de aceitação. Recomendamos que você opte pela serialização de exceção de comunicação remota do DataContract seguindo as etapas deste artigo.

O suporte para serialização de exceção na comunicação remota baseada no BinaryFormatter será preterido no futuro.

Habilitar a serialização de contrato de dados para exceções na comunicação remota

Observação

A serialização de contrato de dados para exceções na comunicação remota só está disponível para serviços de comunicação remota V2/V2_1.

Para habilitar a serialização de contrato de dados para exceções na comunicação remota:

  1. Habilite a serialização de exceção na comunicação remota do DataContract no lado do Serviço usando FabricTransportRemotingListenerSettings.ExceptionSerializationTechnique ao criar o ouvinte de comunicação remota.

    • StatelessService

      protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
      {
          return new[]
          {
              new ServiceInstanceListener(serviceContext =>
                  new FabricTransportServiceRemotingListener(
                      serviceContext,
                      this,
                      new FabricTransportRemotingListenerSettings
                      {
                          ExceptionSerializationTechnique = FabricTransportRemotingListenerSettings.ExceptionSerialization.Default,
                      }),
                   "ServiceEndpointV2")
          };
      }
      
    • StatefulService

      protected override IEnumerable<ServiceReplicaListener> CreateServiceReplicaListeners()
      {
          return new[]
          {
              new ServiceReplicaListener(serviceContext =>
                  new FabricTransportServiceRemotingListener(
                      serviceContext,
                      this,
                      new FabricTransportRemotingListenerSettings
                      {
                          ExceptionSerializationTechnique = FabricTransportRemotingListenerSettings.ExceptionSerialization.Default,
                      }),
                  "ServiceEndpointV2")
          };
      }
      
    • ActorService
      Para habilitar a serialização de exceção na comunicação remota do DataContract no serviço de ator, substitua CreateServiceReplicaListeners() estendendo ActorService.

      protected override IEnumerable<ServiceReplicaListener> CreateServiceReplicaListeners()
      {
          return new List<ServiceReplicaListener>
          {
              new ServiceReplicaListener(_ =>
              {
                  return new FabricTransportActorServiceRemotingListener(
                      this,
                      new FabricTransportRemotingListenerSettings
                      {
                          ExceptionSerializationTechnique = FabricTransportRemotingListenerSettings.ExceptionSerialization.Default,
                      });
              },
              "MyActorServiceEndpointV2")
          };
      }
      

    Se a exceção original tiver vários níveis de exceções internas, você poderá controlar o número de níveis de exceções internas a serem serializados configurando FabricTransportRemotingListenerSettings.RemotingExceptionDepth.

  2. Habilitar a serialização de exceção na comunicação remota do DataContract no Cliente usando FabricTransportRemotingSettings.ExceptionDeserializationTechnique ao criar o alocador de cliente.

    • Criação de ServiceProxyFactory

      var serviceProxyFactory = new ServiceProxyFactory(
      (callbackClient) =>
      {
          return new FabricTransportServiceRemotingClientFactory(
              new FabricTransportRemotingSettings
              {
                  ExceptionDeserializationTechnique = FabricTransportRemotingSettings.ExceptionDeserialization.Default,
              },
              callbackClient);
      });
      
    • ActorProxyFactory

      var actorProxyFactory = new ActorProxyFactory(
      (callbackClient) =>
      {
          return new FabricTransportActorRemotingClientFactory(
              new FabricTransportRemotingSettings
              {
                  ExceptionDeserializationTechnique = FabricTransportRemotingSettings.ExceptionDeserialization.Default,
              },
              callbackClient);
      });
      
  3. A serialização de exceção de comunicação remota do DataContract converte uma exceção no DTO (objeto de transferência de dados) no lado do serviço. O DTO é convertido novamente em uma exceção no lado do cliente. Os usuários precisam registrar ExceptionConvertor para converter as exceções desejadas em objetos DTO e vice-versa.

    A estrutura implementa conversores para a lista de exceções a seguir. Se o código de serviço do usuário depender de exceções fora da lista a seguir para implementação de repetição e tratamento de exceção, o usuário precisará implementar e registrar conversores para essas exceções.

    • Todas as exceções do Service Fabric derivadas de System.Fabric.FabricException
    • SystemExceptions derivadas de System.SystemException
      • System.AccessViolationException
      • System.AppDomainUnloadedException
      • System.ArgumentException
      • System.ArithmeticException
      • System.ArrayTypeMismatchException
      • System.BadImageFormatException
      • System.CannotUnloadAppDomainException
      • System.Collections.Generic.KeyNotFoundException
      • System.ContextMarshalException
      • System.DataMisalignedException
      • System.ExecutionEngineException
      • System.FormatException
      • System.IndexOutOfRangeException
      • System.InsufficientExecutionStackException
      • System.InvalidCastException
      • System.InvalidOperationException
      • System.InvalidProgramException
      • System.IO.InternalBufferOverflowException
      • System.IO.InvalidDataException
      • System.IO.IOException
      • System.MemberAccessException
      • System.MulticastNotSupportedException
      • System.NotImplementedException
      • System.NotSupportedException
      • System.NullReferenceException
      • System.OperationCanceledException
      • System.OutOfMemoryException
      • System.RankException
      • System.Reflection.AmbiguousMatchException
      • System.Reflection.ReflectionTypeLoadException
      • System.Resources.MissingManifestResourceException
      • System.Resources.MissingSatelliteAssemblyException
      • System.Runtime.InteropServices.ExternalException
      • System.Runtime.InteropServices.InvalidComObjectException
      • System.Runtime.InteropServices.InvalidOleVariantTypeException
      • System.Runtime.InteropServices.MarshalDirectiveException
      • System.Runtime.InteropServices.SafeArrayRankMismatchException
      • System.Runtime.InteropServices.SafeArrayTypeMismatchException
      • System.Runtime.Serialization.SerializationException
      • System.StackOverflowException
      • System.Threading.AbandonedMutexException
      • System.Threading.SemaphoreFullException
      • System.Threading.SynchronizationLockException
      • System.Threading.ThreadInterruptedException
      • System.Threading.ThreadStateException
      • System.TimeoutException
      • System.TypeInitializationException
      • System.TypeLoadException
      • System.TypeUnloadedException
      • System.UnauthorizedAccessException
      • System.ArgumentNullException
      • System.IO.FileNotFoundException
      • System.IO.DirectoryNotFoundException
      • System.ObjectDisposedException
      • System.AggregateException

Implementação de exemplo de um conversor do lado do serviço para uma exceção personalizada

O exemplo a seguir é uma implementação de referência IExceptionConvertor no lado do Serviço e do Cliente para um tipo de exceção CustomException bem conhecido.

  • CustomException

    class CustomException : Exception
    {
        public CustomException(string message, string field1, string field2)
            : base(message)
        {
            this.Field1 = field1;
            this.Field2 = field2;
        }
    
        public CustomException(string message, Exception innerEx, string field1, string field2)
            : base(message, innerEx)
        {
            this.Field1 = field1;
            this.Field2 = field2;
        }
    
        public string Field1 { get; set; }
    
        public string Field2 { get; set; }
    }
    
  • Implementação de IExceptionConvertor no lado do Serviço:

    class CustomConvertorService : Microsoft.ServiceFabric.Services.Remoting.V2.Runtime.IExceptionConvertor
    {
        public Exception[] GetInnerExceptions(Exception originalException)
        {
            return originalException.InnerException == null ? null : new Exception[] { originalException.InnerException };
        }
    
        public bool TryConvertToServiceException(Exception originalException, out ServiceException serviceException)
        {
            serviceException = null;
            if (originalException is CustomException customEx)
            {
                serviceException = new ServiceException(customEx.GetType().FullName, customEx.Message);
                serviceException.ActualExceptionStackTrace = originalException.StackTrace;
                serviceException.ActualExceptionData = new Dictionary<string, string>()
                    {
                        { "Field1", customEx.Field1 },
                        { "Field2", customEx.Field2 },
                    };
    
                return true;
            }
    
            return false;
        }
    }
    

A exceção real observada durante a execução da chamada de comunicação remota é passada como entrada para TryConvertToServiceException. Se o tipo da exceção for bem conhecido, TryConvertToServiceException deve converter a exceção original em ServiceException e retorná-la como um parâmetro de saída. Um valor true deverá ser retornado se o tipo de exceção original for bem conhecido e a exceção original for convertida com êxito em ServiceException. Caso contrário, o valor será false.

Uma lista de exceções internas no nível atual deve ser retornada por GetInnerExceptions().

  • Implementação de IExceptionConvertor no lado do Cliente:

    class CustomConvertorClient : Microsoft.ServiceFabric.Services.Remoting.V2.Client.IExceptionConvertor
    {
        public bool TryConvertFromServiceException(ServiceException serviceException, out Exception actualException)
        {
            return this.TryConvertFromServiceException(serviceException, (Exception)null, out actualException);
        }
    
        public bool TryConvertFromServiceException(ServiceException serviceException, Exception innerException, out Exception actualException)
        {
            actualException = null;
            if (serviceException.ActualExceptionType == typeof(CustomException).FullName)
            {
                actualException = new CustomException(
                    serviceException.Message,
                    innerException,
                    serviceException.ActualExceptionData["Field1"],
                    serviceException.ActualExceptionData["Field2"]);
    
                return true;
            }
    
            return false;
        }
    
        public bool TryConvertFromServiceException(ServiceException serviceException, Exception[] innerExceptions, out Exception actualException)
        {
            throw new NotImplementedException();
        }
    }
    

ServiceException é passado como um parâmetro para TryConvertFromServiceException junto com o innerException[s] convertido. Se o tipo de exceção real, ServiceException.ActualExceptionType, for conhecido, o conversor deverá criar um objeto de exceção real do ServiceException e innerException[s].

  • Registro de IExceptionConvertor no lado do Serviço:

    Para registrar conversores, CreateServiceInstanceListeners deve ser substituído e a lista de classes IExceptionConvertor deve ser passada enquanto você cria a instância RemotingListener.

    • StatelessService

      protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
      {
          return new[]
          {
              new ServiceInstanceListener(serviceContext =>
                  new FabricTransportServiceRemotingListener(
                      serviceContext,
                      this,
                      new FabricTransportRemotingListenerSettings
                      {
                          ExceptionSerializationTechnique = FabricTransportRemotingListenerSettings.ExceptionSerialization.Default,
                      },
                      exceptionConvertors: new[]
                      {
                          new CustomConvertorService(),
                      }),
                   "ServiceEndpointV2")
          };
      }
      
    • StatefulService

      protected override IEnumerable<ServiceReplicaListener> CreateServiceReplicaListeners()
      {
          return new[]
          {
              new ServiceReplicaListener(serviceContext =>
                  new FabricTransportServiceRemotingListener(
                      serviceContext,
                      this,
                      new FabricTransportRemotingListenerSettings
                      {
                          ExceptionSerializationTechnique = FabricTransportRemotingListenerSettings.ExceptionSerialization.Default,
                      },
                      exceptionConvertors: new []
                      {
                          new CustomConvertorService(),
                      }),
                  "ServiceEndpointV2")
          };
      }
      
    • ActorService

      protected override IEnumerable<ServiceReplicaListener> CreateServiceReplicaListeners()
      {
          return new List<ServiceReplicaListener>
          {
              new ServiceReplicaListener(_ =>
              {
                  return new FabricTransportActorServiceRemotingListener(
                      this,
                      new FabricTransportRemotingListenerSettings
                      {
                          ExceptionSerializationTechnique = FabricTransportRemotingListenerSettings.ExceptionSerialization.Default,
                      },
                      exceptionConvertors: new[]
                      {
                          new CustomConvertorService(),
                      });
              },
              "MyActorServiceEndpointV2")
          };
      }
      
  • Registro de IExceptionConvertor no lado do Cliente:

    Para registrar conversores, a lista de classes IExceptionConvertor deve ser passada enquanto você cria a instância ClientFactory.

    • Criação de ServiceProxyFactory

      var serviceProxyFactory = new ServiceProxyFactory(
      (callbackClient) =>
      {
         return new FabricTransportServiceRemotingClientFactory(
             new FabricTransportRemotingSettings
             {
                 ExceptionDeserializationTechnique = FabricTransportRemotingSettings.ExceptionDeserialization.Default,
             },
             callbackClient,
             exceptionConvertors: new[]
             {
                 new CustomConvertorClient(),
             });
      });
      
    • Criação de ActorProxyFactory

      var actorProxyFactory = new ActorProxyFactory(
      (callbackClient) =>
      {
          return new FabricTransportActorRemotingClientFactory(
              new FabricTransportRemotingSettings
              {
                  ExceptionDeserializationTechnique = FabricTransportRemotingSettings.ExceptionDeserialization.Default,
              },
              callbackClient,
              exceptionConvertors: new[]
              {
                  new CustomConvertorClient(),
              });
      });
      

Observação

Se a estrutura encontrar o conversor para a exceção, a exceção convertida (real) será encapsulada em AggregateException e lançada na API (proxy) de comunicação remota. Se a estrutura não encontrar o conversor, então ServiceException, que contém todos os detalhes da exceção real, será encapsulado em AggregateException e será gerado.

Atualizar um serviço existente para habilitar a serialização do contrato de dados para exceções de comunicação remota

Os serviços existentes devem seguir a ordem a seguir (Serviço primeiro) para atualizar. A falha em seguir a ordem a seguir pode resultar em mau comportamento na lógica de repetição e tratamento de exceção.

  1. Implemente as classes ExceptionConvertor do lado do Serviço para as exceções desejadas, se houver. Atualize a lógica de registro do ouvinte de comunicação remota com ExceptionSerializationTechnique e a lista de classes IExceptionConvertor. Atualize o serviço existente para aplicar as alterações de serialização de exceção.

  2. Implemente as classes ExceptionConvertor do lado do Cliente para as exceções desejadas, se houver. Atualize a lógica de criação do ProxyFactory com ExceptionSerializationTechnique e a lista de classes IExceptionConvertor. Atualize o cliente existente para aplicar as alterações de serialização de exceção.

Próximas etapas