• WCF扩展之实现ZeroMQ绑定和protocolBuffer消息编码(一)概要设计


     
    在我工作的项目中含有多种操作系统、多种设备、多种开发语言,因此需要使用跨平台的通信技术和自定义的消息编码。经过技术调研,ZeroMQ+ProtocolBuffer最终成为通信技术和编码方式。但是如何使用这些技术成了问题,如果直接调用,势必会让业务逻辑和通信技术绑定在一起,很难分离。所以需要引入一种框架,可以将业务和通信解耦。WCF是目前最成熟的通信框架之一,WCF的优点还是很多的。
     
    WCF连接各种通信技术,即可以封装各种通信技术。无论使用何种技术,设计服务端的方式是一致的;另一方面设计客户端的方式也是一致的,体现在:
    • 客户端调用的方式是一致的
    • 服务端处理请求的方式是一致的
    • 服务的实例管理的方式是一致的
    • 设计服务的操作模式的方式是一致的
    • 如果将自定义数据作为服务的参数,数据的定义是一致的
    • 处理异常的方式是一致的
    • 如果连接的通信技术支持事物,事物的处理是一致的
    WCF可以隔离业务层和技术层,体现在:
    • 当客户端调用服务时,可以不需要知道服务端的实现语言和平台
    • 当服务端处理请求时,可以不需要知道客户端的实现语言和平台
    • 实现WCF的服务端时,只需要考虑业务接口的实现,另外接口和方法添加少量的属性、在配置中添加服务的地址
    • 实现WCF的客户端时,只需要按照协议调用服务,另外在配置中添加服务的地址
     
    但是WCF默认绑定都是基于windows的,无法直接使用WCF内置的绑定。所以我就想既然WCF能集成MSMQ、tcp等一些通信技术,为什么我不能扩展WCF,并让其也集成ZeroMQ呢?
     
    有想法了,说干就干。
     
    经过近两周的努力,WCF的ZMQ扩展基本实现,能使用WCF的客户端或服务端与ZMQ的服务端或客户端相互通信。现在就将整体的设计介绍给大家。由于实现的步骤很多,这将会是一系列的文章。
     
    WCF to ZMQ架构
     
    部署图
    WCF的ZMQ扩展的部署很简单,扩展分为ZMQBinding.dll和ProtocolBufferMessageExtension.dll两部分。ZMQBinding.dll负责将ZMQ集成到WCF中;ProtocolBufferMessageExtension.dll负责通信的数据编码和解析。这两部分将编码和传输分离开,使得编码和传输分别可以用其他方式实现。
     
    组件图
    当把WCF作为服务端,ZMQ作为客户端时,组件图为
     
     当把ZMQ作为服务端,WCF作为客户端时,组件图为:
    无论哪种情况,WCF的服务端和客户端都只需要使用WCF的接口,而不需要了解ZMQ的技术细节。ZMQBinding和ProtocolBufferMessageExtension分别通过配置文件的方式注册到WCF中。当ZMQ发生请求或响应时,通过ZMQBinding中的ZMQ部分连接。ProtocolBufferMessageExtension的格式化器负责将消息体序列化或反序列化成基本类型或者ProtocolBuffer的数据访问类。消息验证器负责验证消息体的正确性,并把消息分发找到合适的WCF服务类的相应服务方法中。消息编码器负责将protocolBuffer的数据访问类编码成二进制数据。
     
    使用WCF to ZMQ
    虽然ZMQBinding和ProtocolBufferMessageExtension的做了很多转换工作,但是一旦将它们注册到WCF中,应用开发人员的工作量很小。
    • 当把WCF作为服务端,ZMQ作为客户端时,WCF服务端需要如下步骤:
    1 定义WCF服务协议
     
       [ServiceContract]
        public interface IService1
        {
            [OperationContract]
            string UpdatePatients(WCF_Client_ZMQBinding.iStationMovementPos move);
        }
    2 实现服务类
       
     public class Service1 : IService1
        {
            public string UpdatePatients(WCF_Client_ZMQBinding.iStationMovementPos move)
            {
                return "wcf response";
            }
        }
    3 定义服务协议所需要的业务实体类,如果服务协议中不含有复杂的实体类,这步可以省略。作为服务方法的参数,不需要添加DataContract属性,是因为自定义了数据的编码,而不是采用xml序列化的方式。

    自定义数据编码是采用了反射的方式,把iStationMovementPos转成protocolBuffer的可序列化的类类型。

        public class iStationMovementPos
        {
            public int IhMaxPos{get;set;}           
            public int IhPos   {get;set;}               
            public int IhMinPos{get;set;}              
     
            public int IvMaxPos{get;set;}              
            public int IvPos   {get;set;}              
            public int IvMinPos{get;set;}              
     
            public int IaMaxPos{get;set;}              
            public int IaPos   {get;set;}              
            public int IaMinPos{get;set;}              
     
            public bool LaserStatus {get;set;}
            public int CouchMarkPos { get; set; }         
        }
    4 通过配置工具在配置文件中配置服务的地址、绑定类型(使用自定义的ZMQBindnig)、协议名(IService1)、服务行为(注册ProtocolBufferMessageExtension)
    5 启动服务
    ServiceHost host = new ServiceHost(typeof(Service1));
    host.Open();
    可见WCF的服务端使用起来非常方便,基本都是业务代码,没有涉及到连接技术(唯一指定连接技术的就是配置文件)。而客户端是完全的ZeroMQ代码,不需要任何WCF的依赖,客户端可以是Android、也可以是linux。
     
    • 当把ZMQ作为服务端,WCF作为客户端时,WCF客户端需要如下步骤:
    WCF客户端和WCF的服务端的步骤相似,服务接口和服务类如下:
            [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
            [System.ServiceModel.ServiceContractAttribute(ConfigurationName = "ServiceReference1.IService1")]
            public interface IService1
            {
     
                [System.ServiceModel.OperationContractAttribute(Action = "http://tempuri.org/IService1/DoWork", ReplyAction = "http://tempuri.org/IService1/DoWorkResponse")]
                [ProtocolBufferMessageExtension.Formatter.PBFormatMessage]
                string DoWork(iStationMovementPos move);
     
                [System.ServiceModel.OperationContractAttribute(Action = "http://tempuri.org/IService1/DoWork", ReplyAction = "http://tempuri.org/IService1/DoWorkResponse")]
                System.Threading.Tasks.Task<string> DoWorkAsync(iStationMovementPos move);
            }
     
            [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
            public interface IService1Channel : Client.ServiceReference1.IService1, System.ServiceModel.IClientChannel
            {
            }
     
            [System.Diagnostics.DebuggerStepThroughAttribute()]
            [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
            public partial class Service1Client : System.ServiceModel.ClientBase<Client.ServiceReference1.IService1>, Client.ServiceReference1.IService1
            {
     
                public Service1Client()
                {
                }
     
                public Service1Client(string endpointConfigurationName) :
                    base(endpointConfigurationName)
                {
                }
     
                public Service1Client(string endpointConfigurationName, string remoteAddress) :
                    base(endpointConfigurationName, remoteAddress)
                {
                }
     
                public Service1Client(string endpointConfigurationName, System.ServiceModel.EndpointAddress remoteAddress) :
                    base(endpointConfigurationName, remoteAddress)
                {
                }
     
                public Service1Client(System.ServiceModel.Channels.Binding binding, System.ServiceModel.EndpointAddress remoteAddress) :
                    base(binding, remoteAddress)
                {
                }
                public string DoWork(iStationMovementPos move)
                {
                    return base.Channel.DoWork(move);
                }
                public System.Threading.Tasks.Task<string> DoWorkAsync(iStationMovementPos move)
                {
                    return base.Channel.DoWorkAsync(move);
                }
            }
    也是通过配置工具在配置文件中配置。
    调用代码非常简单:
                iStationMovementPos move = new iStationMovementPos()
                {
                    IhMaxPos = 100,
                    IhMinPos = 10,
                    IaPos = 1,
                    IaMaxPos = 2,
                    IaMinPos = 3,
                    IhPos = 4,
                    IvMaxPos = 5,
                    IvMinPos = 6,
                    IvPos = 7,
                    LaserStatus = true,
                    CouchMarkPos = 200
                };
                Service1Client client = new Service1Client();
                string res = client.DoWork(move);
    而服务端是完全的ZeroMQ代码,不需要任何WCF的依赖,服务端可以是linux平台的GPC。
    第一篇暂时介绍到这。
  • 相关阅读:
    141. 环形链表
    15. 三数之和
    剑指 Offer 59
    177. 第N高的薪水
    176. 第二高的薪水
    175. 组合两个表
    剑指 Offer 57
    剑指 Offer 56
    110. 平衡二叉树
    置顶
  • 原文地址:https://www.cnblogs.com/polinzhuo/p/5278943.html
Copyright © 2020-2023  润新知