这是这个系列的第三篇,其他的文章请点击下列目录

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

WCF扩展之实现ZeroMQ绑定和protocolBuffer消息编码(二)实现IRequestChannel

WCF扩展之实现ZeroMQ绑定和protocolBuffer消息编码(三)实现ReplyChannel

相对于RequestChannel,ReplyChannel比较复杂一些。

1 启动zmq的rep结点

首先需要重载OnOpen方法,启动zmq的rep结点,主要是调用createSocket方法和绑定地址。

protected override void OnOpen(TimeSpan timeout)
{
if (this.socket == null)
{
this.socket = this.zmqContext.CreateSocket(SocketType.REP);
int trimIPEnd = this.localAddress.Uri.AbsoluteUri.LastIndexOf(':');
string trimIP = this.localAddress.Uri.AbsoluteUri.Substring(trimIPEnd,this.localAddress.Uri.AbsoluteUri.Length - trimIPEnd);
string zmqServerAddress = "tcp://*" + trimIP;
socket.Bind(zmqServerAddress);
}
}

2 实现ReceiveReqeust,返回context

和RequestChannel一样,实现同步版本的ReceiveRequest。此方法是IReplyChannel接口的方法。为什么接口的方法不返回message,而是返回requestContext呢?原因是WCF在收到requestContext后,可以根据RequestContext发送回复消息。

        public RequestContext ReceiveRequest(TimeSpan timeout)
{
ThrowIfDisposedOrNotOpen(); Message request = this.ReceiveMessage(timeout);
return new ZMQRequestContext(this, request, timeout);
}
ReceiveMessage的实现为了复用,放在了基类ZMQChannelBase中
        public Message ReceiveMessage(TimeSpan timeout)
{
base.ThrowIfDisposedOrNotOpen();
byte[] replyData = bufferManager.TakeBuffer(); int replaySize = socket.Receive(replyData); Message response = encoder.ReadMessage(
new ArraySegment<byte>(replyData, , replaySize), bufferManager);
bufferManager.ReturnBuffer(replyData);
return response;
}

3 同步版本的ReceiveRequest实现后,再实现异步版本的

WCF使用ReplyChannel接收消息,默认调用的是BeginReceiveRequest。

异步版本的实现也是使用.Net的AMP异步模式。调用的步骤如下图所示:由上向下执行。远程请求首先进入ReplyChannel的BeginTryReceiveRequest方法,此方法返回TryReceiveRequestAsyncResult实例。然后依次向下执行,直到在BaseChannel.SocketReceiveAsyncResult 中执行ZMQ的socket.Receive()方法。

                                                              远程请求
|
ReplyChannel BeginTryReceiveRequest
|
TryReceiveRequestAsyncResult     new
|
BaseChannel BeginReceiveRequest
|
ReceiveRequestAsyncResult   new
|
BaseChannel BeginReceiveMessage
                                        |
BaseChannel BeginReadData
|
BaseChannel.SocketReceiveAsyncResult         new
|
异步代理执行socket.Receive
|
BaseChannel EndReadData
|
BaseChannel EndReceiveMessage 此处将消息反序列化成数据

步骤很多,每一步都有意义,BeginTryReceiveRequest首先接到请求消息,同时处理超时的情况,使其不会抛出异常。转给BeginReceiveRequest,创建ReceiveRequestAsyncResult对象,在其构造中调用基类的BeginReceiveMessage。基类的BeginReceiveMessage纯粹是为了代码的复用性。转给BeginReadData,创建SocketReceiveAsyncResult对象,是真正启动socket.Receive(),其中使用了异步委托的方式实现了异步。

 
4 解决zmq的同步限制
必要的接口都实现后,可以启动wcf服务来接收zmq客户端的请求了。收到消息后,又一次执行到socket.Receive(),尝试再次接收消息时,出现了异常。异常显示“结点在目前的状态下无法执行此操作”。由于是第一次使用Zmq,不太了解ZMQ的机制。第一次执行socket.Receive()能正常接收消息,第二次执行socket.Receive()就会出错。我又查看了zmq的demo,也执行到第二个socket.Receive(),没有这样的问题。通过比较相同时间下的socket状态发现:
 
       我的程序
socket在接收消息后,
ReceiveStatus: Received
还没有回复返回值,所以:
SendStatus: None zmq的demo
socket在接收消息后,
ReceiveStatus: Received
回复返回值
SendStatus: Sent

zmq的REP socket必须接收请求,发送返回后,才能再次接收请求。我的程序中由于调用了wcf的服务,一直是在调试状态,因此没有及时返回,造成了SendStatus是none,所以不能再次发送。

解决这个问题也很简单,使用了ManualResetEvent。在接收消息后,将ManualResetEvent置成reset状态,在receive()之前调用ManualResetEvent的WaitOne(),等待发送返回。一旦replyChannel发送返回后,立刻将ManualResetEvent置成set状态,就执行到了receive()。

接收时

                    serviceHanledDone.WaitOne();
int receiveLength = socket.Receive(data1);
serviceHanledDone.Reset();
return receiveLength;

发送后,立刻将ManualResetEvent置成set,使得waitone放行。

                    socket.Send(data);
serviceHanledDone.Set();

5 添加zmq队列支持

至此,zmqBinding可以接收到zmq客户端的请求,并能正确的返回。但是似乎一次只能接收一个请求,等待回复后,才能接收下一个请求。虽然wcf的处理都是异步的,但是zmq的rep结点限制了服务端的处理能力,那么怎么能接收多个请求呢?zmq既然叫做“mq”,是有队列的功能的。通过zmq的手册知道,router-dealer是可以实现队列的功能的。我使用的zmq版本是clrzmq,但是网上没有clrzmq实现router-dealder的例子代码。在clrzmq的源码中的测试代码中,我发现了clrzmq的QueueDevice类实现了zmq的router-dealer模式。而且使用起来很简单。完整的zmq例子请参见我的其他文章。

这里注意一点,就是QueueDevice应该首先启动,然后再启动REP 结点。因此还使用ManualResetEvent对象。QueueDecvice在一个新建的线程中启动,启动后,通知REP节点启动。代码是这样的:

protected override void OnOpen(TimeSpan timeout)
{
if (this.socket == null)
{
startRouterDealer(this.zmqContext); _deviceReady.WaitOne(); this.socket = this.zmqContext.CreateSocket(SocketType.REP);
int trimIPEnd = this.localAddress.Uri.AbsoluteUri.LastIndexOf(':');
string trimIP = this.localAddress.Uri.AbsoluteUri.Substring(trimIPEnd,this.localAddress.Uri.AbsoluteUri.Length - trimIPEnd);
string zmqServerAddress = "tcp://*" + trimIP;
//socket.Bind(zmqServerAddress);
socket.Connect("inproc://backend");
}
}
protected override void OnClosing()
{
base.OnClosing();
}
private static void startRouterDealer(ZmqContext context)
{
ThreadPool.QueueUserWorkItem(new WaitCallback(startQueueDeviceThread), context);
}
private static void startQueueDeviceThread(object state)
{
ZmqContext context = state as ZmqContext;
//Thread.Sleep(2000);
using (QueueDevice queue = new QueueDevice(context,
"tcp://*:5555",
"inproc://backend",
DeviceMode.Threaded))
{
queue.Initialize();
_deviceReady.Set();
queue.Start();
while (true)
{
Thread.Sleep();
}
} }

至此,ZMQBinding的Transport部分就完成了。下一篇开始介绍protocolBuffer消息编码,以及在wcf中如何编码和解码。

WCF扩展之实现ZeroMQ绑定和protocolBuffer消息编码(三)实现ReplyChannel(2016-03-15 12:35)的更多相关文章

  1. WCF扩展之实现ZeroMQ绑定和protocolBuffer消息编码(二)实现IRequestChannel(2016-03-15 12:35)

    这是这个系列的第二篇,其他的文章请点击下列目录 WCF扩展之实现ZeroMQ绑定和protocolBuffer消息编码(一)概要设计 WCF扩展之实现ZeroMQ绑定和protocolBuffer消息 ...

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

      在我工作的项目中含有多种操作系统.多种设备.多种开发语言,因此需要使用跨平台的通信技术和自定义的消息编码.经过技术调研,ZeroMQ+ProtocolBuffer最终成为通信技术和编码方式.但是如 ...

  3. WCF扩展系列 - 行为扩展(Behaviors)

    原文地址:http://www.cnblogs.com/Creator/archive/2011/05/21/2052687.html 这个系列的第一部分将会重点关注WCF行为(behaviors), ...

  4. 【转】WCF扩展系列 - 行为扩展(Behaviors)

    原文:https://www.cnblogs.com/Creator/archive/2011/05/21/2052687.html 这个系列的第一部分将会重点关注WCF行为(behaviors),W ...

  5. 真实世界:使用WCF扩展记录服务调用时间

    WCF 可扩展性 WCF 提供了许多扩展点供开发人员自定义运行时行为. WCF 在 Channel Layer 之上还提供了一个高级运行时,主要是针对应用程序开发人员.在 WCF 文档中,它常被称为服 ...

  6. WCF 服务的ABC之绑定(六)

    绑定 Binding 绑定是开发人员控制WCF程序与其他消息交互的主要手段.从功能上看,绑定创建了通道工厂惑通道侦听器的堆栈对象.绑定直接惑间接创建的对象是WCF实现各种消息功能(例如,传输.安全性. ...

  7. WCF扩展

    WCF 可扩展性 WCF 提供了许多扩展点供开发人员自定义运行时行为. WCF 在 Channel Layer 之上还提供了一个高级运行时,主要是针对应用程序开发人员.在 WCF 文档中,它常被称为服 ...

  8. 扩展SpringMVC以支持绑定JSON格式的请求参数

    此方案是把请求参数(JSON字符串)绑定到java对象,,@RequestBody是绑定内容体到java对象的. 问题描述: <span style="font-size: x-sma ...

  9. WCF 项目应用连载[8] - 绑定、服务、行为 大数据传输与限流 - 下 (ServiceThrottlingAttribute)

    因为ORM的原因,对Attribute编程有一种情节..所以这节的出现,完全是因为在WCF对自定义Attribute的一种应用. WCF 项目应用连载[7] - 绑定.服务.行为 大数据传输与限流 - ...

随机推荐

  1. efwplus框架

    此框架得到博客园大神@张善友的关注,建议我写一篇此框架的最新介绍,好在@dotNet跨平台公众号上推荐给大家,得到大神的指示当然激动,马不停蹄的赶出此文,供大家参考!   一.使用efwplus框架的 ...

  2. WM_SYSCOMMAND消息命令整理 good

    注意:1. 使用WM_SYSCOMMAND时,鼠标的一些消息可能会受到影响,比如不能响应MouseUp事件,可以在窗口中捕获WM_SYSCOMMAND消息,并判断消息的CommandType来判断消息 ...

  3. thinkphp 3.2.3 入门示例2(URL传参数的几种方式)

    原文:thinkphp中URL传参数的几种方式 在thinkphp中,url传参合asp.net中原理类似,下面就单个参数和多个参数传递方式进行一个简单讲解 1.传单个参数 单个参数这种比较简单,例如 ...

  4. 14.3.3 Locks Set by Different SQL Statements in InnoDB 不同的SQL语句在InnoDB里的锁设置

    14.3.3 Locks Set by Different SQL Statements in InnoDB 不同的SQL语句在InnoDB里的锁设置 locking read, 一个UPDATE,或 ...

  5. MySQL多表查询之外键、表连接、子查询、索引

    MySQL多表查询之外键.表连接.子查询.索引 一.外键: 1.什么是外键 2.外键语法 3.外键的条件 4.添加外键 5.删除外键 1.什么是外键: 主键:是唯一标识一条记录,不能有重复的,不允许为 ...

  6. leetcode回文子串拆分-最小拆分次数

    转载请注明来自souldak,微博:@evagle 上一篇是要输出所有的可能拆分,这回是要输出拆分次数最少的切割次数. 如果直接按照上一篇那么做的话,就会超时,因为我们在判断s[i][j]是否是回文的 ...

  7. 不知道的JavaScript

    你不知道的JavaScript上卷笔记 前言 You don't know JavaScript是github上一个系列文章  初看到这一标题的时候,感觉怎么老外也搞标题党,用这种冲突性比较强的题目吸 ...

  8. win8 远程桌面 你得凭证不工作

    今天在在登录azure远程桌面时,一直提示你的凭证不工作, 按照网上各种教程都不行, 后来发现在win8中用户名结构中,需要加上本机电脑名比如:“win8\administrator”.

  9. Do you master on array in C ?

    Do you master on array in C ? 因为新标准C99的支持变长数组, 差点儿C的标准特性就是看着gcc来的(Linux 内核严重依赖GCC) int mani() { cons ...

  10. WebService 通过POST方式访问时候,因 URL 意外地以“/方法名”结束,请求格式无法识别 解决办法

    因URL意外地以“/方法名”结束,请求格式无法识别. 执行当前Web请求期间生成了未处理的异常.可以使用下面的异常堆栈跟踪信息确定有关异常原因和发生位置的信息. 解决方法:在webservice的we ...