远程处理异常序列化概述

基于 BinaryFormatter 的序列化不安全,因此不要使用 BinaryFormatter 进行数据处理。 有关安全影响的详细信息,请参阅 BinaryFormatter 和相关类型的使用中的反序列化风险。

Azure Service Fabric 使用 BinaryFormatter 序列化异常。 从 ServiceFabric v9.0 开始,用于远程处理异常的基于数据协定的序列化便作为可选功能提供。 建议按照本文中的步骤选择 DataContract 远程处理异常序列化。

未来将弃用对基于 BinaryFormatter 的远程处理异常序列化的支持。

启用用于远程处理异常的数据协定序列化

注意

用于远程处理异常的数据协定序列化仅适用于远程处理 V2/V2_1 服务。

启用用于远程处理异常的数据协定序列化:

  1. 在创建远程处理侦听器时,使用 FabricTransportRemotingListenerSettings.ExceptionSerializationTechnique 在服务端启用 DataContract 远程处理异常序列化。

    • 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
      若要在 ActorService 上启用 DataContract 远程处理异常序列化,请通过扩展 ActorService 替代 CreateServiceReplicaListeners()。

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

    如果原始异常具有多个级别的内部异常,可以通过设置 FabricTransportRemotingListenerSettings.RemotingExceptionDepth 来控制要序列化的内部异常的级别数。

  2. 在创建客户端工厂时,使用 FabricTransportRemotingSettings.ExceptionDeserializationTechnique 在客户端启用 DataContract 远程处理异常序列化。

    • 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. DataContract 远程处理异常序列化会将异常转换为服务端的数据传输对象 (DTO)。 DTO 将转换回客户端上的异常。 用户需要注册 ExceptionConvertor 以将所需的异常转换为 DTO 对象,反之亦然。

    框架为以下异常列表实现转换器。 如果用户服务代码依赖于以下列表之外的异常进行重试实现和异常处理,则用户需要实现和注册此类异常的转换器。

    • 所有 Service Fabric 异常(派生自 System.Fabric.FabricException)
    • SystemExceptions(派生自 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

自定义异常的服务端转换器的示例实现

下面示例是已知异常类型 CustomException 在“服务”和“客户”端的参考 IExceptionConvertor 实现。

  • 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 实现:

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

在执行远程处理调用期间观察到的实际异常作为输入传递给 TryConvertToServiceException。 如果异常的类型是已知类型,则 TryConvertToServiceException 应将原始异常转换为 ServiceException 并将其作为输出参数返回。 如果原始异常类型为已知类型,并且原始异常已成功转换为 ServiceException,则应返回 true 值。 否则,该值为 false。

当前级别的内部异常列表应由 GetInnerExceptions() 返回。

  • “客户”端的 IExceptionConvertor 实现:

    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 作为参数与转换的 innerException[s] 一起传递给 TryConvertFromServiceException。 如果实际异常类型 ServiceException.ActualExceptionType 为已知类型,则转换器应从 ServiceException 和 innerException[s] 创建实际异常对象。

  • “服务”端的 IExceptionConvertor 注册:

    若要注册转换程序,必须重写 CreateServiceInstanceListeners,并且必须在创建 RemotingListener 实例时传递 IExceptionConvertor 类列表。

    • 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 注册:

    若要注册转换程序,必须在创建 ClientFactory 实例时传递 IExceptionConvertor 类列表。

    • ServiceProxyFactory 创建

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

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

注意

如果框架找到异常的转换器,则转换的(实际)异常将包装在 AggregateException 中,并在远程处理 API(代理)引发。 如果框架找不到转换器,则包含实际异常的所有详细信息的 ServiceException 将包装在 AggregateException 中并引发。

升级现有服务以启用用于远程处理异常的数据协定序列化

现有服务必须遵循以下顺序(“服务优先”)进行升级。 未能按照以下顺序操作可能会导致重试逻辑和异常处理发生错误行为。

  1. 为所需的异常(如果有)实现“服务”端 ExceptionConvertor 类。 采用 ExceptionSerializationTechnique 和 IExceptionConvertor 类列表更新远程处理侦听器注册逻辑。 升级现有服务以应用异常序列化更改。

  2. 为所需的异常(如果有)实现“客户”端 ExceptionConvertor 类。 采用 ExceptionSerializationTechnique 和 IExceptionConvertor 类列表更新 ProxyFactory 创建逻辑。 升级现有客户端以应用异常序列化更改。

后续步骤