Descrição geral da serialização da exceção remota

A serialização baseada em BinaryFormatter não é segura, por isso não utilize BinaryFormatter para processamento de dados. Para obter mais informações sobre as implicações de segurança, veja Riscos de desserialização na utilização de BinaryFormatter e tipos relacionados.

O Azure Service Fabric utilizou BinaryFormatter para serializar exceções. A partir do ServiceFabric v9.0, a serialização baseada em contratos de dados para exceções remotas está disponível como uma funcionalidade de opção ativa de ativação. Recomendamos que opte pela serialização da exceção de comunicação remota DataContract ao seguir os passos neste artigo.

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

Ativar a serialização de contratos de dados para exceções remotas

Nota

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

Para ativar a serialização de contratos de dados para exceções remotas:

  1. Ative a serialização da exceção de comunicação remota DataContract no lado do Serviço ao utilizar FabricTransportRemotingListenerSettings.ExceptionSerializationTechnique enquanto cria o serviço de escuta 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 ativar a serialização da exceção remota DataContract no serviço de ator, substitua CreateServiceReplicaListeners() ao expandir 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, pode controlar o número de níveis de exceções internas a serializar ao definir FabricTransportRemotingListenerSettings.RemotingExceptionDepth.

  2. Ative a serialização da exceção de comunicação remota DataContract no Cliente ao utilizar FabricTransportRemotingSettings.ExceptionDeserializationTechnique enquanto cria a fábrica de cliente.

    • Criação do 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 da exceção remota DataContract converte uma exceção no objeto de transferência de dados (DTO) do lado do serviço. O DTO é convertido novamente numa exceção no lado do cliente. Os utilizadores têm de se registar ExceptionConvertor para converter as exceções pretendidas em objetos DTO e vice-versa.

    A arquitetura implementa conversores para a seguinte lista de exceções. Se o código de serviço do utilizador depender de exceções fora da lista seguinte para implementação de repetições e processamento de exceções, os utilizadores terão de implementar e registar conversores para essas exceções.

    • Todas as exceções do Service Fabric derivadas de System.Fabric.FabricException
    • SystemExceptions derivado de System.SystemException
      • System.AccessViolationException
      • System.AppDomainUnloadedException
      • System.ArgumentException
      • System.AithmeticException
      • 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 seguinte é a implementação de referência IExceptionConvertor no lado do Serviço e do 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 do 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 é transmitida como entrada para TryConvertToServiceException. Se o tipo da exceção for bem conhecido, TryConvertToServiceException deve converter a exceção original em ServiceException e devolvê-la como um parâmetro de saída. Deve ser devolvido um valor verdadeiro 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 é falso.

Uma lista de exceções internas no nível atual deve ser devolvida 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 é transmitido como um parâmetro para TryConvertFromServiceException juntamente 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 registar os conversores, CreateServiceInstanceListeners tem de ser substituída e a lista de classes tem de IExceptionConvertor ser transmitida enquanto 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 registar os conversores, a lista de classes tem de IExceptionConvertor ser transmitida enquanto cria a ClientFactory instância.

    • Criação do 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 arquitetura encontrar o conversor para a exceção, a exceção convertida (real) é encapsulada no interior AggregateException e é emitida na API remota (proxy). Se a arquitetura não conseguir localizar o conversor, ServiceExceptionentão , que contém todos os detalhes da exceção real, é encapsulado no interior AggregateException e é emitido.

Atualizar um serviço existente para ativar a serialização de contratos de dados para exceções remotas

Os serviços existentes têm de seguir a seguinte ordem (Serviço primeiro) para atualizar. O não cumprimento desta ordem pode resultar num comportamento incorreto na lógica de repetição e no processamento de exceções.

  1. Implemente as classes do lado do ServiçoExceptionConvertor para as exceções pretendidas, se existirem. Atualize a lógica de registo do serviço de escuta remota com ExceptionSerializationTechnique e a lista de IExceptionConvertorclasses. Atualize o serviço existente para aplicar as alterações de serialização da exceção.

  2. Implemente as classes do Lado ExceptionConvertor do cliente para as exceções pretendidas, se existirem. 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 da exceção.

Passos seguintes