forked from zeromq/clrzmq4
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmdcliapi.cs
205 lines (170 loc) · 7.06 KB
/
mdcliapi.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
using System;
using System.Threading;
using ZeroMQ;
namespace Examples
{
namespace MDCliApi
{
//
// mdcliapi class - Majordomo Protocol Client API
// Implements the MDP/Worker spec at http://rfc.zeromq.org/spec:7.
//
// Author: metadings
//
public class MajordomoClient : IDisposable
{
// Structure of our class
// We access these properties only via class methods
// Our context
readonly ZContext _context;
// Majordomo broker
public string Broker { get; protected set; }
// Socket to broker
public ZSocket Client { get; protected set; }
// Print activity to console
public bool Verbose { get; protected set; }
// Request timeout
public TimeSpan Timeout { get; protected set; }
// Request retries
public int Retries { get; protected set; }
public void ConnectToBroker()
{
// Connect or reconnect to broker
Client = new ZSocket(_context, ZSocketType.REQ);
Client.Connect(Broker);
if (Verbose)
"I: connecting to broker at '{0}'...".DumpString(Broker);
}
public MajordomoClient(string broker, bool verbose)
{
if(broker == null)
throw new InvalidOperationException();
_context = new ZContext();
Broker = broker;
Verbose = verbose;
Timeout = TimeSpan.FromMilliseconds(2500);
Retries = 3;
ConnectToBroker();
}
~MajordomoClient()
{
Dispose(false);
}
public void Dispose()
{
GC.SuppressFinalize(this);
Dispose(true);
}
protected void Dispose(bool disposing)
{
if (disposing)
{
// Destructor
if (Client != null)
{
Client.Dispose();
Client = null;
}
//Do not Dispose Context: cuz of weird shutdown behavior, stucks in using calls
}
}
// .split configure retry behavior
// These are the class methods. We can set the request timeout and number
// of retry attempts before sending requests:
// Set request timeout
public void Set_Timeout(int timeoutInMs)
{
Timeout = TimeSpan.FromMilliseconds(timeoutInMs);
}
// Set request retries
public void Set_Retries(int retries)
{
Retries = retries;
}
// .split send request and wait for reply
// Here is the {{send}} method. It sends a request to the broker and gets
// a reply even if it has to retry several times. It takes ownership of
// the request message, and destroys it when sent. It returns the reply
// message, or NULL if there was no reply after multiple attempts:
public ZMessage Send(string service, ZMessage request, CancellationTokenSource cancellor)
{
if (request == null)
throw new InvalidOperationException();
// Prefix request with protocol frames
// Frame 1: "MDPCxy" (six bytes, MDP/Client x.y)
// Frame 2: Service name (printable string)
request.Prepend(new ZFrame(service));
request.Prepend(new ZFrame(MdpCommon.MDPC_CLIENT));
if (Verbose)
request.DumpZmsg("I: send request to '{0}' service:", service);
int retriesLeft = Retries;
while (retriesLeft > 0 && !cancellor.IsCancellationRequested)
{
if (cancellor.IsCancellationRequested
|| (Console.KeyAvailable && Console.ReadKey(true).Key == ConsoleKey.Escape))
_context.Shutdown();
// Copy the Request and send on Client
ZMessage msgreq = request.Duplicate();
ZError error;
if (!Client.Send(msgreq, out error))
{
if (Equals(error, ZError.ETERM))
{
cancellor.Cancel();
break; // Interrupted
}
}
var p = ZPollItem.CreateReceiver();
ZMessage msg;
// .split body of send
// On any blocking call, {{libzmq}} will return -1 if there was
// an error; we could in theory check for different error codes,
// but in practice it's OK to assume it was {{EINTR}} (Ctrl-C):
// Poll the client Message
if (Client.PollIn(p, out msg, out error, Timeout))
{
// If we got a reply, process it
if (Verbose)
msg.DumpZmsg("I: received reply");
if(msg.Count < 3)
throw new InvalidOperationException();
using (ZFrame header = msg.Pop())
if (!header.ToString().Equals(MdpCommon.MDPC_CLIENT))
throw new InvalidOperationException();
using (ZFrame replyService = msg.Pop())
if(!replyService.ToString().Equals(service))
throw new InvalidOperationException();
request.Dispose();
return msg;
}
else if (Equals(error, ZError.ETERM))
{
cancellor.Cancel();
break; // Interrupted
}
else if (Equals(error, ZError.EAGAIN))
{
if (--retriesLeft > 0)
{
if (Verbose)
"W: no reply, reconnecting...".DumpString();
ConnectToBroker();
}
else
{
if (Verbose)
"W: permanent error, abandoning".DumpString();
break; // Give up
}
}
}
if (cancellor.IsCancellationRequested)
{
"W: interrupt received, killing client...\n".DumpString();
}
request.Dispose();
return null;
}
}
}
}