/// <summary> /// an idempotent method processing all requests to close a request with a GUID /// it is safe to call it multiple times with the same GUID /// </summary> internal void ProcessTitanicClose([CanBeNull] IMDPWorker mdpWorker = null) { // get a MDP worker with an automatic id and register with the service "titanic.Close" // the worker will automatically start and connect to MDP Broker with the indicated address using (var worker = mdpWorker ?? new MDPWorker(m_titanicAddress, TitanicOperation.Close.ToString())) { NetMQMessage reply = null; while (true) { // initiate the communication with sending a null, since there is no reply yet var request = worker.Receive(reply); Log(string.Format("[TITANIC CLOSE] received: {0}", request)); //! has there been a breaking cause? -> exit if (ReferenceEquals(request, null)) { break; } // we expect [Guid] as the only frame var guidAsString = request.Pop().ConvertToString(); var guid = Guid.Parse(guidAsString); Log(string.Format("[TITANIC CLOSE] closing {0}", guid)); // close the request m_io.CloseRequest(guid); // send back the confirmation reply = new NetMQMessage(); reply.Push(TitanicReturnCode.Ok.ToString()); } } }
// ======================= HELPER ========================= private void EchoWorker(IMDPWorker worker, int heartbeatinterval = 2500) { worker.HeartbeatDelay = TimeSpan.FromMilliseconds(heartbeatinterval); // send the reply and wait for a request var request = worker.Receive(null); // was the worker interrupted Assert.That(request, Is.Not.Null); // echo the request and wait for next request which will not come // here the task will be canceled var newrequest = worker.Receive(request); }
/// <summary> /// process any titanic reply request by a client /// /// <para>will send an OK, PENDING or UNKNOWN as result of the request for the reply</para> /// </summary> internal void ProcessTitanicReply([CanBeNull] IMDPWorker mdpWorker = null) { // get a MDP worker with an automatic id and register with the service "titanic.reply" // the worker will automatically start and connect to the indicated address using (var worker = mdpWorker ?? new MDPWorker(m_titanicAddress, TitanicOperation.Reply.ToString())) { NetMQMessage reply = null; while (true) { // initiate the communication to MDP Broker with sending a 'null', // since there is no initial reply everytime thereafter the reply will be send var request = worker.Receive(reply); Log(string.Format("TITANIC REPLY] received: {0}", request)); //! has there been a breaking cause? -> exit if (ReferenceEquals(request, null)) { break; } var requestIdAsString = request.Pop().ConvertToString(); var requestId = Guid.Parse(requestIdAsString); if (m_io.ExistsMessage(TitanicOperation.Reply, requestId)) { Log(string.Format("[TITANIC REPLY] reply for request exists: {0}", requestId)); reply = m_io.GetMessage(TitanicOperation.Reply, requestId); // [service][reply] reply.Push(TitanicReturnCode.Ok.ToString()); // ["OK"][service][reply] } else { reply = new NetMQMessage(); var replyCommand = (m_io.ExistsMessage(TitanicOperation.Request, requestId) ? TitanicReturnCode.Pending : TitanicReturnCode.Unknown); reply.Push(replyCommand.ToString()); } Log(string.Format("[TITANIC REPLY] reply: {0}", reply)); } } }
private void DoubleEchoWorker(IMDPWorker worker, int heartbeatinterval = 2500) { worker.HeartbeatDelay = TimeSpan.FromMilliseconds(heartbeatinterval); var request = worker.Receive(null); // was the worker interrupted Assert.That(request, Is.Not.Null); // double the content of the request var payload = request.Last.ConvertToString(); payload += " - " + payload; request.RemoveFrame(request.Last); request.Append(payload); // echo the request and wait for next request which will not come // here the task will be canceled var newrequest = worker.Receive(request); }
private static void AddHelloWorker(IMDPWorker worker, int heartbeatinterval = 2500) { worker.HeartbeatDelay = TimeSpan.FromMilliseconds(heartbeatinterval); // send the reply and wait for a request var request = worker.Receive(null); // was the worker interrupted Assert.That(request, Is.Not.Null); // add the extra the content to request var payload = request.Last.ConvertToString(); payload += " - HELLO"; request.RemoveFrame(request.Last); request.Append(payload); // echo the request and wait for next request which will not come // here the task will be canceled var newrequest = worker.Receive(request); }
/// <summary> /// process a titanic request according to TITANIC Protocol /// /// <para>it connects via provided PAIR socket to main thread</para> /// <para>write request to disk and return the GUID to client</para> /// sends the GUID of the request back via the pipe for further processing /// </summary> internal void ProcessTitanicRequest([NotNull] PairSocket pipe, [CanBeNull] IMDPWorker mdpWorker = null) { // get a MDP worker with an automatic id and register with the service "titanic.request" // the worker will automatically start and connect to a MDP Broker at the indicated address using (var worker = mdpWorker ?? new MDPWorker(m_titanicAddress, TitanicOperation.Request.ToString())) { NetMQMessage reply = null; while (true) { // initiate the communication with sending a 'null', since there is no initial reply // a request should be [service name][request data] var request = worker.Receive(reply); Log(string.Format("[TITANIC REQUEST] Received request: {0}", request)); //! has there been a breaking cause? -> exit if (ReferenceEquals(request, null)) { break; } //! check if service exists! and return 'Unknown' if not // generate Guid for the request var requestId = Guid.NewGuid(); // save request to file -> [service name][request data] m_io.SaveMessage(TitanicOperation.Request, requestId, request); Log(string.Format("[TITANIC REQUEST] sending through pipe: {0}", requestId)); // send GUID through message queue to main thread pipe.Send(requestId.ToString()); // return GUID via reply message via worker.Receive call reply = new NetMQMessage(); // [Guid] reply.Push(requestId.ToString()); // [Ok][Guid] reply.Push(TitanicReturnCode.Ok.ToString()); Log(string.Format("[TITANIC REQUEST] sending reply: {0}", reply)); } } }
private static void MultipleRequestWorker(IMDPWorker worker = null, string endpoint = null, int heartbeatinterval = 2500) { var idW01 = new[] { (byte)'W', (byte)'1' }; worker = worker ?? new MDPWorker(endpoint, "echo", idW01); worker.HeartbeatDelay = TimeSpan.FromMilliseconds(heartbeatinterval); NetMQMessage reply = null; while (true) { // send the reply and wait for a request var request = worker.Receive(reply); // was the worker interrupted Assert.That(request, Is.Not.Null); // echo the request and wait for next request which will not come // here the task will be canceled reply = worker.Receive(request); } }
private void AddHelloWorker(IMDPWorker worker, int heartbeatinterval = 2500) { worker.HeartbeatDelay = TimeSpan.FromMilliseconds(heartbeatinterval); // send the reply and wait for a request var request = worker.Receive(null); // was the worker interrupted Assert.That(request, Is.Not.Null); // add the extra the content to request var payload = request.Last.ConvertToString(); payload += " - HELLO"; request.RemoveFrame(request.Last); request.Append(payload); // echo the request and wait for next request which will not come // here the task will be canceled var newrequest = worker.Receive(request); }
private void MultipleRequestWorker(IMDPWorker worker = null, string endpoint = null, int heartbeatinterval = 2500) { var idW01 = new[] { (byte)'W', (byte)'1' }; worker = worker ?? new MDPWorker(endpoint, "echo", idW01); worker.HeartbeatDelay = TimeSpan.FromMilliseconds(heartbeatinterval); NetMQMessage reply = null; while (true) { // send the reply and wait for a request var request = worker.Receive(reply); // was the worker interrupted Assert.That(request, Is.Not.Null); // echo the request and wait for next request which will not come // here the task will be canceled reply = worker.Receive(request); } }
private static void DoubleEchoWorker (IMDPWorker worker, int heartbeatinterval = 2500) { worker.HeartbeatDelay = TimeSpan.FromMilliseconds (heartbeatinterval); var request = worker.Receive (null); // was the worker interrupted Assert.That (request, Is.Not.Null); // double the content of the request var payload = request.Last.ConvertToString (); payload += " - " + payload; request.RemoveFrame (request.Last); request.Append (payload); // echo the request and wait for next request which will not come // here the task will be canceled var newrequest = worker.Receive (request); }
/// <summary> /// process a titanic request according to TITANIC Protocol /// /// <para>it connects via provided PAIR socket to main thread</para> /// <para>write request to disk and return the GUID to client</para> /// sends the GUID of the request back via the pipe for further processing /// </summary> internal void ProcessTitanicRequest( PairSocket pipe, IMDPWorker mdpWorker = null) { // get a MDP worker with an automatic id and register with the service "titanic.request" // the worker will automatically start and connect to a MDP Broker at the indicated address using (var worker = mdpWorker ?? new MDPWorker (m_titanicAddress, TitanicOperation.Request.ToString ())) { NetMQMessage reply = null; while (true) { // initiate the communication with sending a 'null', since there is no initial reply // a request should be [service name][request data] var request = worker.Receive (reply); Log (string.Format ("[TITANIC REQUEST] Received request: {0}", request)); //! has there been a breaking cause? -> exit if (ReferenceEquals (request, null)) break; //! check if service exists! and return 'Unknown' if not // generate Guid for the request var requestId = Guid.NewGuid (); // save request to file -> [service name][request data] m_io.SaveMessage (TitanicOperation.Request, requestId, request); Log (string.Format ("[TITANIC REQUEST] sending through pipe: {0}", requestId)); // send GUID through message queue to main thread pipe.SendFrame (requestId.ToString ()); // return GUID via reply message via worker.Receive call reply = new NetMQMessage (); // [Guid] reply.Push (requestId.ToString ()); // [Ok][Guid] reply.Push (TitanicReturnCode.Ok.ToString ()); Log (string.Format ("[TITANIC REQUEST] sending reply: {0}", reply)); } } }
/// <summary> /// process any titanic reply request by a client /// /// <para>will send an OK, PENDING or UNKNOWN as result of the request for the reply</para> /// </summary> internal void ProcessTitanicReply( IMDPWorker mdpWorker = null) { // get a MDP worker with an automatic id and register with the service "titanic.reply" // the worker will automatically start and connect to the indicated address using (var worker = mdpWorker ?? new MDPWorker (m_titanicAddress, TitanicOperation.Reply.ToString ())) { NetMQMessage reply = null; while (true) { // initiate the communication to MDP Broker with sending a 'null', // since there is no initial reply everytime thereafter the reply will be send var request = worker.Receive (reply); Log (string.Format ("TITANIC REPLY] received: {0}", request)); //! has there been a breaking cause? -> exit if (ReferenceEquals (request, null)) break; var requestIdAsString = request.Pop ().ConvertToString (); var requestId = Guid.Parse (requestIdAsString); if (m_io.ExistsMessage (TitanicOperation.Reply, requestId)) { Log (string.Format ("[TITANIC REPLY] reply for request exists: {0}", requestId)); reply = m_io.GetMessage (TitanicOperation.Reply, requestId); // [service][reply] reply.Push (TitanicReturnCode.Ok.ToString ()); // ["OK"][service][reply] } else { reply = new NetMQMessage (); var replyCommand = (m_io.ExistsMessage (TitanicOperation.Request, requestId) ? TitanicReturnCode.Pending : TitanicReturnCode.Unknown); reply.Push (replyCommand.ToString ()); } Log (string.Format ("[TITANIC REPLY] reply: {0}", reply)); } } }
/// <summary> /// an idempotent method processing all requests to close a request with a GUID /// it is safe to call it multiple times with the same GUID /// </summary> internal void ProcessTitanicClose(IMDPWorker mdpWorker = null) { // get a MDP worker with an automatic id and register with the service "titanic.Close" // the worker will automatically start and connect to MDP Broker with the indicated address using (var worker = mdpWorker ?? new MDPWorker (m_titanicAddress, TitanicOperation.Close.ToString ())) { NetMQMessage reply = null; while (true) { // initiate the communication with sending a null, since there is no reply yet var request = worker.Receive (reply); Log (string.Format ("[TITANIC CLOSE] received: {0}", request)); //! has there been a breaking cause? -> exit if (ReferenceEquals (request, null)) break; // we expect [Guid] as the only frame var guidAsString = request.Pop ().ConvertToString (); var guid = Guid.Parse (guidAsString); Log (string.Format ("[TITANIC CLOSE] closing {0}", guid)); // close the request m_io.CloseRequest (guid); // send back the confirmation reply = new NetMQMessage (); reply.Push (TitanicReturnCode.Ok.ToString ()); } } }
/// <summary> /// <para>is the main thread of the broker</para> /// <para>it spawns threads handling titanic operations</para> /// <para>it receives GUID from Titanic Request Service and dispatches the requests /// to available workers via MDPBroker</para> /// <para>it also manages the appropriate changes in the file system as well as in queue</para> /// </summary> /// <param name="requestWorker">mdp worker processing the incoming requests for services</param> /// <param name="replyWorker">mdp worker processing incoming reply requests</param> /// <param name="closeWorker">mdp worker processing incoming close requests</param> /// <param name="serviceCallClient">mdp client forwarding requests to service providing mdp worker /// via mdp broker and collecting replies</param> /// <exception cref="TerminatingException">The socket has been stopped.</exception> /// <exception cref="AddressAlreadyInUseException">The specified address is already in use.</exception> /// <exception cref="NetMQException">No IO thread was found, or the protocol's listener encountered an /// error during initialization.</exception> /// <exception cref="ObjectDisposedException">thrown if the socket was already disposed</exception> public void Run( IMDPWorker requestWorker = null, IMDPWorker replyWorker = null, IMDPWorker closeWorker = null, IMDPClient serviceCallClient = null) { using (var pipeStart = new PairSocket ()) using (var pipeEnd = new PairSocket ()) using (var cts = new CancellationTokenSource ()) { // set up the inter thread communication pipe pipeStart.Bind (_titanic_internal_communication); pipeEnd.Connect (_titanic_internal_communication); // start the three child tasks var requestTask = Task.Run (() => ProcessTitanicRequest (pipeEnd, requestWorker), cts.Token); var replyTask = Task.Run (() => ProcessTitanicReply (replyWorker), cts.Token); var closeTask = Task.Run (() => ProcessTitanicClose (closeWorker), cts.Token); var tasks = new[] { requestTask, replyTask, closeTask }; while (true) { // wait for 1s for a new request from 'Request' to process var input = pipeStart.Poll (PollEvents.PollIn, TimeSpan.FromMilliseconds (1000)); // any message available? -> process it if ((input & PollEvents.PollIn) == PollEvents.PollIn) { // only one frame will be send [Guid] var msg = pipeStart.ReceiveFrameString (); Guid guid; if (!Guid.TryParse (msg, out guid)) Log ("[TITANIC BROKER] Received a malformed GUID via pipe - throw it away"); else { Log (string.Format ("[TITANIC BROKER] Received request GUID {0} via pipe", msg)); // now we have a valid GUID - save it to disk for further use m_io.SaveNewRequestEntry (guid); } } //! now dispatch (brute force) the requests -> SHOULD BE MORE INTELLIGENT (!) // dispatching will also worry about the handling of a potential reply // dispatch only requests which have not been closed foreach (var entry in m_io.GetNotClosedRequestEntries ().Where (entry => entry != default (RequestEntry))) { if (DispatchRequests (entry.RequestId, serviceCallClient)) m_io.SaveProcessedRequestEntry (entry); } //! should implement some sort of restart // beware of the silently dieing threads - must be detected! if (DidAnyTaskStopp (tasks)) { // stopp all threads cts.Cancel (); // stop processing! break; } } } }
/// <summary> /// <para>is the main thread of the broker</para> /// <para>it spawns threads handling titanic operations</para> /// <para>it receives GUID from Titanic Request Service and dispatches the requests /// to available workers via MDPBroker</para> /// <para>it also manages the appropriate changes in the file system as well as in queue</para> /// </summary> /// <param name="requestWorker">mdp worker processing the incoming requests for services</param> /// <param name="replyWorker">mdp worker processing incoming reply requests</param> /// <param name="closeWorker">mdp worker processing incoming close requests</param> /// <param name="serviceCallClient">mdp client forwarding requests to service providing mdp worker /// via mdp broker and collecting replies</param> /// <exception cref="TerminatingException">The socket has been stopped.</exception> /// <exception cref="AddressAlreadyInUseException">The specified address is already in use.</exception> /// <exception cref="NetMQException">No IO thread was found, or the protocol's listener encountered an /// error during initialization.</exception> /// <exception cref="ObjectDisposedException">thrown if the socket was already disposed</exception> public void Run([CanBeNull] IMDPWorker requestWorker = null, [CanBeNull] IMDPWorker replyWorker = null, [CanBeNull] IMDPWorker closeWorker = null, [CanBeNull] IMDPClient serviceCallClient = null) { using (var ctx = NetMQContext.Create()) using (var pipeStart = ctx.CreatePairSocket()) using (var pipeEnd = ctx.CreatePairSocket()) using (var cts = new CancellationTokenSource()) { // set up the inter thread communication pipe pipeStart.Bind(_titanic_internal_communication); pipeEnd.Connect(_titanic_internal_communication); // start the three child tasks var requestTask = Task.Run(() => ProcessTitanicRequest(pipeEnd, requestWorker), cts.Token); var replyTask = Task.Run(() => ProcessTitanicReply(replyWorker), cts.Token); var closeTask = Task.Run(() => ProcessTitanicClose(closeWorker), cts.Token); var tasks = new[] { requestTask, replyTask, closeTask }; while (true) { // wait for 1s for a new request from 'Request' to process var input = pipeStart.Poll(PollEvents.PollIn, TimeSpan.FromMilliseconds(1000)); // any message available? -> process it if ((input & PollEvents.PollIn) == PollEvents.PollIn) { // only one frame will be send [Guid] var msg = pipeStart.ReceiveFrameString(); Guid guid; if (!Guid.TryParse(msg, out guid)) { Log("[TITANIC BROKER] Received a malformed GUID via pipe - throw it away"); } else { Log(string.Format("[TITANIC BROKER] Received request GUID {0} via pipe", msg)); // now we have a valid GUID - save it to disk for further use m_io.SaveNewRequestEntry(guid); } } //! now dispatch (brute force) the requests -> SHOULD BE MORE INTELLIGENT (!) // dispatching will also worry about the handling of a potential reply // dispatch only requests which have not been closed foreach (var entry in m_io.GetNotClosedRequestEntries().Where(entry => entry != default(RequestEntry))) { if (DispatchRequests(entry.RequestId, serviceCallClient)) { m_io.SaveProcessedRequestEntry(entry); } } //! should implement some sort of restart // beware of the silently dieing threads - must be detected! if (DidAnyTaskStopp(tasks)) { // stopp all threads cts.Cancel(); // stop processing! break; } } } }