Condividi tramite


Panoramica della serializzazione delle eccezioni di comunicazione remota

La serializzazione basata su BinaryFormatter non è sicura, quindi non usare BinaryFormatter per l'elaborazione dei dati. Per altre informazioni sulle implicazioni relative alla sicurezza, vedere Rischi di deserializzazione nell'uso di BinaryFormatter e dei tipi correlati.

Azure Service Fabric usa BinaryFormatter per serializzare le eccezioni. A partire da ServiceFabric v9.0, la serializzazione basata sul contratto dati per le eccezioni di comunicazione remota è disponibile come funzionalità di consenso esplicito. È consigliabile optare per la serializzazione delle eccezioni remote DataContract seguendo la procedura descritta in questo articolo.

Il supporto per la serializzazione delle eccezioni basate su BinaryFormatter sarà deprecato in futuro.

Abilitare la serializzazione del contratto dati per le eccezioni di comunicazione remota

Nota

La serializzazione del contratto dati per le eccezioni remote è disponibile solo per i servizi remoti V2/V2_1.

Per abilitare la serializzazione del contratto dati per le eccezioni di comunicazione remota:

  1. Abilitare la serializzazione delle eccezioni remote DataContract sul lato Servizio usando FabricTransportRemotingListenerSettings.ExceptionSerializationTechnique durante la creazione del listener di comunicazione 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
      Per abilitare la serializzazione delle eccezioni remote DataContract nel servizio actor, eseguire l'override di 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 l'eccezione originale ha più livelli di eccezioni interne, è possibile controllare il numero di livelli di eccezioni interne da serializzare impostando FabricTransportRemotingListenerSettings.RemotingExceptionDepth.

  2. Abilitare la serializzazione delle eccezioni remote DataContract nel Client usando FabricTransportRemotingSettings.ExceptionDeserializationTechnique durante la creazione della factory client.

    • Creazione di 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. La serializzazione dell'eccezione remota DataContract converte un'eccezione nell'oggetto di trasferimento dati (DTO) sul lato servizio. L'oggetto DTO viene convertito nuovamente in un'eccezione sul lato client. Gli utenti devono registrare ExceptionConvertor per convertire le eccezioni desiderate in oggetti DTO e viceversa.

    Il framework implementa i convertitori per l'elenco di eccezioni seguente. Se il codice del servizio utente dipende dalle eccezioni esterne all'elenco seguente per l'implementazione e la gestione delle eccezioni, gli utenti devono implementare e registrare i convertitori per tali eccezioni.

    • Tutte le eccezioni di Service Fabric derivate da System.Fabric.FabricException
    • SystemExceptions derivate da 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

Implementazione di esempio di un convertitore lato servizio per un'eccezione personalizzata

L'esempio seguente è un'implementazione IExceptionConvertor del riferimento sul lato Servizio e Client per un tipo di eccezione noto, 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; }
    }
    
  • Implementazione IExceptionConvertor del lato Servizio:

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

L'eccezione effettiva osservata durante l'esecuzione della chiamata remota viene passata come input a TryConvertToServiceException. Se il tipo dell'eccezione è noto, TryConvertToServiceException deve convertire l'eccezione originale in ServiceException e restituirla come parametro in uscita. Un valore true deve essere restituito se il tipo di eccezione originale è noto e l'eccezione originale viene convertita correttamente in ServiceException. In caso contrario, il valore è false.

Un elenco di eccezioni interne a livello corrente deve essere restituito da GetInnerExceptions().

  • Implementazione IExceptionConvertor del lato Client:

    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 viene passato come parametro a TryConvertFromServiceException insieme ai innerException[s] convertiti. Se il tipo di eccezione effettivo, ServiceException.ActualExceptionType, è noto, il convertitore deve creare un oggetto eccezione effettivo da ServiceException e innerException[s].

  • Registrazione IExceptionConvertor sul lato Servizio:

    Per registrare i convertitori, è necessario eseguire l'override di CreateServiceInstanceListeners e passare l'elenco delle classi IExceptionConvertor durante la creazione dell'istanza di 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")
          };
      }
      
  • Registrazione IExceptionConvertor sul lato Client:

    Per registrare i convertitori, è necessario passare l'elenco delle classi IExceptionConvertor durante la creazione dell'istanza di ClientFactory.

    • ServiceProxyFactory creation

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

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

Nota

Se il framework trova il convertitore per l'eccezione, l'eccezione convertita (effettiva) viene sottoposta a wrapping all'interno di AggregateException e viene generata nell'API remota (proxy). Se il framework non riesce a trovare il convertitore, ServiceException, che contiene tutti i dettagli dell'eccezione effettiva, viene sottoposto a wrapping all'interno di AggregateException e viene generato.

Aggiornare un servizio esistente per abilitare la serializzazione del contratto dati per le eccezioni remote

Per eseguire l'aggiornamento, i servizi esistenti devono attenersi all'ordine seguente (Prima il servizio). Se non si segue questo ordine, potrebbe verificarsi un comportamento errato nella logica di retry e nella gestione delle eccezioni.

  1. Implementare le classi lato ServizioExceptionConvertor per le eccezioni desiderate, se presenti. Aggiornare la logica di registrazione del listener remoto con ExceptionSerializationTechnique e l'elenco delle classi IExceptionConvertor. Aggiornare il servizio esistente per applicare le modifiche alla serializzazione delle eccezioni.

  2. Implementare le classi lato ClientExceptionConvertor per le eccezioni desiderate, se presenti. Aggiornare la logica di creazione di ProxyFactory con ExceptionSerializationTechnique e l'elenco delle classi IExceptionConvertor. Aggiornare il client esistente per applicare le modifiche alla serializzazione delle eccezioni.

Passaggi successivi