zoukankan      html  css  js  c++  java
  • 在 WCF 中使用高效的 BinaryFormatter 序列化

    本文将定义一个 WCF 终结点行为扩展,以在 WCF 中使用更高效的 BinaryFormatter 进行二进制序列化,并实现对是否使用传统二进制序列化功能的可配置。

    • 介绍
    • 实现步骤
    • 使用方法
    • 效果

    介绍


    online_icon[6]OEA 框架中,是使用 WCF 作为数据传输框架。但是使用 WCF 内部的二进制序列化,序列化后的数据大小,要比使用传统的 System.Runtime.Serialization.Formatters.Binary.BinaryFormatter 类进行序列化后的数据大小要大得多。作为使用 .NET 框架的系统内部互联,往往期望在使用 WCF 获取统一传输方案的同时,还能得到 BinaryFormatter 类的序列化性能。所以本篇文章将设计一个 WCF 终结点行为扩展,来配置是否使用 BinaryFormatter 进行数据的序列化。

    alert_note[10] Tip

    只能在操作上添加二进制序列化的行为。这是因为 WCF 的扩展点中,只有操作才支持设置 IClientMessageFormatter 及 IDispatchMessageFormatter。

    WCF 中,要实现替换操作的序列化器,最直接的方式应该是使用一个实现 IOperationBehavior 的特性(Attribute),并将该特性直接标记到操作方法上。但是,这样会导致该方法在所有的终结点都使用 BinaryFormatter 来进行序列化。这并不是我们所想要的,所以只能使用配置的方法来对 WCF 进行扩展。

    实现步骤


    • 封装 BinaryFormatter

    首先,需要对 BinaryFormatter 进行一个简单的封装。该类使用 BinaryFormatter 来实现对象到二进制流的序列化及反序列化。

       1:  /// <summary> 
       2:  /// 序列化门户 API 
       3:  /// </summary> 
       4:  public static class Serializer
       5:  {
       6:      /// <summary> 
       7:      /// 使用二进制序列化对象。 
       8:      /// </summary> 
       9:      /// <param name="value"></param> 
      10:      /// <returns></returns> 
      11:      public static byte[] SerializeBytes(object value)
      12:      {
      13:          if (value == null) return null;
      14:   
      15:          var stream = new MemoryStream();
      16:          new BinaryFormatter().Serialize(stream, value);
      17:   
      18:          //var dto = Encoding.UTF8.GetString(stream.GetBuffer()); 
      19:          var bytes = stream.ToArray();
      20:          return bytes;
      21:      }
      22:   
      23:      /// <summary> 
      24:      /// 使用二进制反序列化对象。 
      25:      /// </summary> 
      26:      /// <param name="bytes"></param> 
      27:      /// <returns></returns> 
      28:      public static object DeserializeBytes(byte[] bytes)
      29:      {
      30:          if (bytes == null) return null;
      31:   
      32:          //var bytes = Encoding.UTF8.GetBytes(dto as string); 
      33:          var stream = new MemoryStream(bytes);
      34:   
      35:          var result = new BinaryFormatter().Deserialize(stream);
      36:   
      37:          return result;
      38:      }
      39:  }
    • 添加 BinaryFormatterAdapter

    添加一个 BinaryFormatterAdapter 类型,该类实现了从 WCF 序列化器到 BinaryFormatter 的甜适配。它实现 IClientMessageFormatter 及 IDispatchMessageFormatter 两个接口,并调用 Serializer 来进行二进制序列化。

       1:  namespace OEA.WCF
       2:  {
       3:      /// <summary> 
       4:      /// 在内部序列化器的基础上添加 Remoting 二进制序列化的功能。 
       5:      /// </summary> 
       6:      internal class BinaryFormatterAdapter : IClientMessageFormatter, IDispatchMessageFormatter
       7:      {
       8:          private IClientMessageFormatter _innerClientFormatter;
       9:          private IDispatchMessageFormatter _innerDispatchFormatter;
      10:          private ParameterInfo[] _parameterInfos;
      11:          private string _operationName;
      12:          private string _action;
      13:  
      14:          /// <summary> 
      15:          /// for client 
      16:          /// </summary> 
      17:          /// <param name="operationName"></param> 
      18:          /// <param name="parameterInfos"></param> 
      19:          /// <param name="innerClientFormatter"></param> 
      20:          /// <param name="action"></param> 
      21:          public BinaryFormatterAdapter(
      22:              string operationName,
      23:              ParameterInfo[] parameterInfos,
      24:              IClientMessageFormatter innerClientFormatter,
      25:              string action
      26:              )
      27:          {
      28:              if (operationName == null) throw new ArgumentNullException("methodName");
      29:              if (parameterInfos == null) throw new ArgumentNullException("parameterInfos");
      30:              if (innerClientFormatter == null) throw new ArgumentNullException("innerClientFormatter");
      31:              if (action == null) throw new ArgumentNullException("action");
      32:  
      33:              this._innerClientFormatter = innerClientFormatter;
      34:              this._parameterInfos = parameterInfos;
      35:              this._operationName = operationName;
      36:              this._action = action;
      37:          }
      38:  
      39:          /// <summary> 
      40:          /// for server 
      41:          /// </summary> 
      42:          /// <param name="operationName"></param> 
      43:          /// <param name="parameterInfos"></param> 
      44:          /// <param name="innerDispatchFormatter"></param> 
      45:          public BinaryFormatterAdapter(
      46:              string operationName,
      47:              ParameterInfo[] parameterInfos,
      48:              IDispatchMessageFormatter innerDispatchFormatter
      49:              )
      50:          {
      51:              if (operationName == null) throw new ArgumentNullException("operationName");
      52:              if (parameterInfos == null) throw new ArgumentNullException("parameterInfos");
      53:              if (innerDispatchFormatter == null) throw new ArgumentNullException("innerDispatchFormatter");
      54:  
      55:              this._innerDispatchFormatter = innerDispatchFormatter;
      56:              this._operationName = operationName;
      57:              this._parameterInfos = parameterInfos;
      58:          }
      59:  
      60:          Message IClientMessageFormatter.SerializeRequest(MessageVersion messageVersion, object[] parameters)
      61:          {
      62:              var result = new object[parameters.Length];
      63:              for (int i = 0; i < parameters.Length; i++) { result[i] = Serializer.SerializeBytes(parameters[i]); }
      64:  
      65:              return _innerClientFormatter.SerializeRequest(messageVersion, result);
      66:          }
      67:  
      68:          object IClientMessageFormatter.DeserializeReply(Message message, object[] parameters)
      69:          {
      70:              var result = _innerClientFormatter.DeserializeReply(message, parameters);
      71:  
      72:              for (int i = 0; i < parameters.Length; i++) { parameters[i] = Serializer.DeserializeBytes(parameters[i] as byte[]); }
      73:              result = Serializer.DeserializeBytes(result as byte[]);
      74:  
      75:              return result;
      76:          }
      77:  
      78:          void IDispatchMessageFormatter.DeserializeRequest(Message message, object[] parameters)
      79:          {
      80:              _innerDispatchFormatter.DeserializeRequest(message, parameters);
      81:  
      82:              for (int i = 0; i < parameters.Length; i++) { parameters[i] = Serializer.DeserializeBytes(parameters[i] as byte[]); }
      83:          }
      84:  
      85:          Message IDispatchMessageFormatter.SerializeReply(MessageVersion messageVersion, object[] parameters, object result)
      86:          {
      87:              var seralizedParameters = new object[parameters.Length];
      88:              for (int i = 0; i < parameters.Length; i++) { seralizedParameters[i] = Serializer.SerializeBytes(parameters[i]); }
      89:              var serialzedResult = Serializer.SerializeBytes(result);
      90:  
      91:              return _innerDispatchFormatter.SerializeReply(messageVersion, seralizedParameters, serialzedResult);
      92:          }
      93:      }
      94:  }
    • 添加 BinaryFormatterOperationBehavior

    添加 BinaryFormatterOperationBehavior 操作行为类。这个类会设置客户端、服务端的操作的序列化器。

       1:  namespace OEA.WCF
       2:  {
       3:      /// <summary> 
       4:      /// 在原始 Formatter 的基础上装饰 BinaryFormatterAdapter 
       5:      /// <remarks> 
       6:      /// BinaryFormatterOperationBehavior 为什么要实现为操作的行为: 
       7:      /// 因为只有当操作的 DataContractSerializerBehavior 行为应用功能后,才能拿到 DataContractSerializerFormatter 并包装到 BinaryFormatterAdapter 中。 
       8:      ///  
       9:      /// 由于一个操作的操作契约在系统中只有一份。而我们期望序列化的行为只影响指定的终结点,所以这个行为在应用时,会检查是否传入的运行时,即是添加时的运行时。 
      10:      /// </remarks> 
      11:      /// </summary> 
      12:      internal class BinaryFormatterOperationBehavior : IOperationBehavior
      13:      {
      14:          private object _runtime;
      15:   
      16:          internal BinaryFormatterOperationBehavior(object runtime)
      17:          {
      18:              _runtime = runtime;
      19:          }
      20:   
      21:          /// <summary> 
      22:          /// 本行为只为这个运行时起作用。 
      23:          /// </summary> 
      24:          public object ParentRuntime
      25:          {
      26:              get { return _runtime; }
      27:          }
      28:   
      29:          public void ApplyClientBehavior(OperationDescription description, ClientOperation runtime)
      30:          {
      31:              if (_runtime == runtime.Parent)
      32:              {
      33:                  //在之前的创建的 Formatter 的基础上,装饰新的 Formatter
      34:                  runtime.Formatter = new BinaryFormatterAdapter(description.Name, runtime.SyncMethod.GetParameters(), runtime.Formatter, runtime.Action);
      35:              }
      36:          }
      37:   
      38:          public void ApplyDispatchBehavior(OperationDescription description, DispatchOperation runtime)
      39:          {
      40:              if (_runtime == runtime.Parent)
      41:              {
      42:                  runtime.Formatter = new BinaryFormatterAdapter(description.Name, description.SyncMethod.GetParameters(), runtime.Formatter);
      43:              }
      44:          }
      45:   
      46:          public void AddBindingParameters(OperationDescription description, BindingParameterCollection parameters) { }
      47:   
      48:          public void Validate(OperationDescription description) { }
      49:      }
      50:  }
    • 添加终结点行为 EnableBinaryFormatterBehavior

    添加终结点行为 EnableBinaryFormatterBehavior,实现为该终结点下的所有操作添加 BinaryFormatterOperationBehavior 的逻辑。

       1:  namespace OEA.WCF
       2:  {
       3:      class EnableBinaryFormatterBehavior : IEndpointBehavior
       4:      {
       5:          public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
       6:          {
       7:              foreach (var operation in endpoint.Contract.Operations)
       8:              {
       9:                  DecorateFormatterBehavior(operation, clientRuntime);
      10:              }
      11:          }
      12:   
      13:          public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
      14:          {
      15:              foreach (var operation in endpoint.Contract.Operations)
      16:              {
      17:                  DecorateFormatterBehavior(operation, endpointDispatcher.DispatchRuntime);
      18:              }
      19:          }
      20:   
      21:          public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters) { }
      22:   
      23:          public void Validate(ServiceEndpoint endpoint) { }
      24:   
      25:          private static void DecorateFormatterBehavior(OperationDescription operation, object runtime)
      26:          {
      27:              //这个行为附加一次。 
      28:              var dfBehavior = operation.Behaviors.Find<BinaryFormatterOperationBehavior>();
      29:              if (dfBehavior == null)
      30:              {
      31:                  //装饰新的操作行为 
      32:                  //这个行为是操作的行为,但是我们期望只为当前终结点做操作的序列化,所以传入 runtime 进行区分。
      33:                  dfBehavior = new BinaryFormatterOperationBehavior(runtime);
      34:                  operation.Behaviors.Add(dfBehavior);
      35:              }
      36:          }
      37:      }
      38:  }
    • 添加行为扩展元素 EnableBinaryFormatterBehaviorElement

    添加终结点行为扩展类,使得该类在配置文件可以使用。它指定了对应的运行时行为类型是 EnableBinaryFormatterBehavior

       1:  namespace OEA.WCF
       2:  {
       3:      /// <summary> 
       4:      /// 启用旧的 BinaryFormatter 来对数据进行序列化。 
       5:      /// </summary> 
       6:      public class EnableBinaryFormatterBehaviorElement : BehaviorExtensionElement
       7:      {
       8:          public override Type BehaviorType
       9:          {
      10:              get { return typeof(EnableBinaryFormatterBehavior); }
      11:          }
      12:   
      13:          protected override object CreateBehavior()
      14:          {
      15:              return new EnableBinaryFormatterBehavior();
      16:          }
      17:      }
      18:  }

    使用方法


    要使用这个扩展,只需要在客户端、服务端做相应的配置即可:

    • 服务端配置

    在 system.serviceModel 中添加扩展及行为配置:

       1:  <system.serviceModel> 
       2:      <behaviors> 
       3:          <endpointBehaviors> 
       4:              <behavior name="enableRemotingBinarySerialization"> 
       5:                  <remotingBinarySerialization/> 
       6:              </behavior> 
       7:          </endpointBehaviors> 
       8:      </behaviors> 
       9:      <extensions> 
      10:          <behaviorExtensions> 
      11:              <add name="remotingBinarySerialization" type="OEA.WCF.EnableBinaryFormatterBehaviorElement, OEA"/> 
      12:          </behaviorExtensions> 
      13:      </extensions> 
      14:  </system.serviceModel>

    为服务终结点添加行为配置 behaviorConfiguration="enableRemotingBinarySerialization"。

       1:  <system.serviceModel>
       2:      <services>
       3:          <service name="OEA.Server.Hosts.WcfPortal" behaviorConfiguration="includesException">
       4:              <endpoint address="/Binary" binding="customBinding" bindingConfiguration="compactBindingConfig"
       5:                          behaviorConfiguration="enableRemotingBinarySerialization"
       6:                          contract="OEA.Server.Hosts.IWcfPortal"/>
       7:          </service>
       8:      </services>
       9:  </system.serviceModel>
    • 客户端

      客户端同样添加相应的扩展及行为配置,并添加到服务终结点上即可。

    • 效果


      效果图:

      WCF Binary Serializer - 序列化数据量效果图[7]

      以上是使用公司目前正在开发的系统的数据量进行测试的结果。可以看到,使用 WCF 直接二进制序列化时,32000 行数据序列化后大小是 28.34M(黄底),而启用这个扩展进行序列化后大小是 13.89M(浅绿底)。当同时使用 WCF 二进制序列化及 BinaryFormatter 序列化后,数据大小是10.42 M(绿底)。

      alert_note[11]Note

      同时使用多次序列化,虽然数据量会更小,但是序列化时间却增多。使用时,需要根据实际情况来调整。

  • 相关阅读:
    CodeForces 347B Fixed Points (水题)
    CodeForces 347A Difference Row (水题)
    CodeForces 346A Alice and Bob (数学最大公约数)
    CodeForces 474C Captain Marmot (数学,旋转,暴力)
    CodeForces 474B Worms (水题,二分)
    CodeForces 474A Keyboard (水题)
    压力测试学习(一)
    算法学习(一)五个常用算法概念了解
    C#语言规范
    异常System.Threading.Thread.AbortInternal
  • 原文地址:https://www.cnblogs.com/zgynhqf/p/3153010.html
Copyright © 2011-2022 走看看