This is the third article in this series, other articles please click the following directory
Implementation of WCF Extensions ZEROMQ binding and Protocolbuffer message encoding (i) Overview design
Implementation of the WCF extension ZEROMQ binding and Protocolbuffer message encoding (ii) Implementation IRequestChannel
Implementation of the WCF extension ZEROMQ binding and Protocolbuffer message encoding (c) Implementation Replychannel
It's more complicated than Requestchannel,replychannel.
1 Starting the ZMQ Rep node
First, you need to overload the OnOpen method to start the ZMQ rep node, primarily calling the Createsocket method and the binding address.
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 implement Receivereqeust, return to context
As with Requestchannel, a synchronous version of Receiverequest is implemented. This method is a method of the IReplyChannel interface. Why does the interface method return RequestContext instead of returning a message? The reason is that WCF can send a reply message based on RequestContext after it receives requestcontext.
public RequestContext ReceiveRequest(TimeSpan timeout)
{
ThrowIfDisposedOrNotOpen();
Message request = this.ReceiveMessage(timeout); return new ZMQRequestContext(this, request, timeout);
}
The implementation of ReceiveMessage is used in the base class zmqchannelbase in order to reuse.
public Message ReceiveMessage(TimeSpan timeout)
{ base.ThrowIfDisposedOrNotOpen(); byte[] replyData = bufferManager.TakeBuffer(1000); int replaySize = socket.Receive(replyData);
Message response = encoder.ReadMessage( new ArraySegment<byte>(replyData, 0, replaySize), bufferManager);
bufferManager.ReturnBuffer(replyData); return response;
}
3 Synchronous version of the Receiverequest implementation, and then implement the asynchronous version of the
WCF uses Replychannel to receive messages, and Beginreceiverequest is called by default.
The implementation of the asynchronous version is also used. NET amp async mode. The steps to invoke are as follows: performed from top to bottom. The remote request first enters Replychannel's Begintryreceiverequest method, which returns the Tryreceiverequestasyncresult instance. Then execute down in turn until the Socket.receive () method of ZMQ is executed in Basechannel.socketreceiveasyncresult.
Remote request
A kind of
ReplyChannel BeginTryReceiveRequest
A kind of
TryReceiveRequestAsyncResult new
A kind of
BaseChannel BeginReceiveRequest
A kind of
ReceiveRequestAsyncResult new
A kind of
BaseChannel BeginReceiveMessage
A kind of
BaseChannel BeginReadData
A kind of
BaseChannel.SocketReceiveAsyncResult new
A kind of
Asynchronous agent executes socket.receive
A kind of
BaseChannel EndReadData
A kind of
Basechannel endreceivemessage deserialize message to data here
A lot of steps, every step has meaning, Begintryreceiverequest first receives a request message and handles the timeout so that it does not throw an exception. Transfer to Beginreceiverequest, create a Receiverequestasyncresult object, and call the base class's beginreceivemessage in its construction. The beginreceivemessage of the base class is purely for the reusability of the code. Transfer to Beginreaddata, create Socketreceiveasyncresult object, is really start socket. Receive (), where asynchronous delegates are used in a way that implements Asynchrony.
4 Resolve ZMQ synchronization limits after the necessary interfaces are implemented, you can start the WCF service to receive requests from the ZMQ client. Once the message is received, it is executed again to the socket. Receive (), an exception occurred while trying to receive the message again. The exception display "node cannot perform this operation in its current state." Because it is the first time to use ZMQ, I do not understand the mechanism of ZMQ. Executes the socket for the first time. Receive () Receives the message normally and executes the socket for the second time. Receive () will go wrong. I also checked the demo of ZMQ and executed to the second socket. Receive (), there is no such problem. By comparing the socket state discovery at the same time:
My program
After the socket receives the message,
ReceiveStatus: Received
The return value has not been returned, so: my program
After the socket receives the message,
ReceiveStatus: Received
The return value has not been returned, so:
SendStatus: None
Demo of ZMQ
After the socket receives the message,
ReceiveStatus: Received
Reply return value
SendStatus: Sent
SendStatus: None
Demo of ZMQ
After the socket receives the message,
ReceiveStatus: Received
Reply return value
SendStatus: Sent
The ZMQ rep socket must receive the request and send it back before it can receive the request again. My program due to the invocation of the WCF service, has been in the debug state, so did not return in time, resulting in sendstatus is none, so can not be sent again.
Solving this problem is also very simple, using ManualResetEvent. After receiving the message, place the ManualResetEvent in the reset state and call ManualResetEvent's WaitOne () before receive (), waiting for the send to return. Once the Replychannel sends the return, the ManualResetEvent is placed in the set state immediately and the receive () is executed.
When receiving
serviceHanledDone.WaitOne();
int receiveLength = socket.Receive(data1);
serviceHanledDone.Reset();
return receiveLength;
After sending, the ManualResetEvent is placed into set immediately, allowing the WaitOne to be released.
Socket. Send (data); Servicehanleddone.set ();
5 adding ZMQ queue support
At this point, zmqbinding can receive a request from the ZMQ client and return it correctly. However, it seems that only one request can be received at a time, waiting for a reply before receiving the next request. While WCF processing is asynchronous, ZMQ's rep node limits the processing power of the server, so how can I receive multiple requests? Since ZMQ is called "MQ", it has the function of queue. The ZMQ manual knows that Router-dealer is capable of implementing a queue. The ZMQ version I use is CLRZMQ, but there is no CLRZMQ implementation of Router-dealder example code on the Web. In the CLRZMQ code, I found that the Clrzmq Queuedevice class implements the Router-dealer mode of ZMQ. And it's easy to use. See my other articles for a complete ZMQ example.
Note here that Queuedevice should start first and then start the rep node. Therefore, the ManualResetEvent object is also used. Queuedecvice starts in a new thread and notifies the rep node when it starts. The code is this:
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(1000);
}
}
}
So far, the transport part of Zmqbinding has been completed. The next article begins with the Protocolbuffer message encoding and how it is encoded and decoded in WCF.
Implementation of the WCF extension ZEROMQ binding and Protocolbuffer message encoding (c) Implementation Replychannel (2016-03-15 12:35)