Partilhar via


Visão geral da serialização de exceções remotas

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

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

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

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

Nota

A serialização de contrato de dados para exceções de 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 de comunicação remota:

  1. Habilite a serialização de exceção remota DataContract no lado Serviço usando FabricTransportRemotingListenerSettings.ExceptionSerializationTechnique enquanto cria o ouvinte remoto.

    • 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 remota DataContract no serviço ator, substitua CreateServiceReplicaListeners() estendendo ActorServiceo .

      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 serializadas pela configuração FabricTransportRemotingListenerSettings.RemotingExceptionDepth.

  2. Habilite a serialização de exceção remota DataContract no cliente usando FabricTransportRemotingSettings.ExceptionDeserializationTechnique enquanto você cria a fábrica do 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 remota DataContract converte uma exceção para o objeto de transferência de dados (DTO) no lado do serviço. O DTO é convertido novamente em uma exceção no lado do cliente. Os usuários precisam se registrar ExceptionConvertor para converter as exceções desejadas em objetos DTO e vice-versa.

    A estrutura implementa conversores para a seguinte lista de exceções. Se o código de serviço do usuário depender de exceções fora da lista a seguir para implementação de novas tentativas e tratamento de exceções, os usuários precisarão 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

Exemplo de implementação de um conversor do lado do serviço para uma exceção personalizada

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

  • 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; }
    }
    
  • IExceptionConvertor implementação 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 remota é passada como entrada para TryConvertToServiceException. Se o tipo de exceção for bem conhecido, TryConvertToServiceException deve converter a exceção original e ServiceException devolvê-la como um parâmetro out. Um valor verdadeiro deve ser retornado se o tipo de exceção original for conhecido e a exceção original for convertida com êxito em ServiceException. Caso contrário, o valor é false.

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

  • IExceptionConvertor implementação do 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 convertido innerException[s]. Se o tipo de exceção real, ServiceException.ActualExceptionType, for conhecido, o conversor deverá criar um objeto de exceção real de ServiceException e innerException[s].

  • IExceptionConvertor registo no lado do Serviço :

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

    • 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")
          };
      }
      
  • IExceptionConvertor registo do lado do Cliente :

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

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

Nota

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

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

Os serviços existentes devem seguir a seguinte ordem (Serviço primeiro) para atualizar. O não cumprimento dessa ordem pode resultar em mau comportamento na lógica de repetição e no tratamento de exceções.

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

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

Próximos passos