/// <summary> /// dispatch the request with the specified GUID th the next available worker if any /// </summary> /// <param name="requestId">request's GUID</param> /// <param name="serviceClient">mdp client to handle mdp broker communication</param> /// <returns><c>true</c> if successfull <c>false</c> otherwise</returns> internal bool DispatchRequests(Guid requestId, [CanBeNull] IMDPClient serviceClient = null) { // has the request already been processed? -> file does not exist // threat this as successfully processed if (!m_io.ExistsMessage(TitanicOperation.Request, requestId)) { Log(string.Format("[TITANIC DISPATCH] Request {0} does not exist. Removing it from queue.", requestId)); // close request inorder to avoid any further processing m_io.CloseRequest(requestId); return(true); } // load request from file var request = m_io.GetMessage(TitanicOperation.Request, requestId); // [service name][data] var serviceName = request[0].ConvertToString(); Log(string.Format("[TITANIC DISPATCH] Do a ServiceCall for {0} - {1}.", serviceName, request)); var reply = ServiceCall(serviceName, request, serviceClient); if (ReferenceEquals(reply, null)) { return(false); // no reply } // a reply has been received -> save it Log(string.Format("[TITANIC DISPATCH] Saving reply for request {0}.", requestId)); // save the reply for further use m_io.SaveMessage(TitanicOperation.Reply, requestId, reply); return(true); }
/// <summary> /// carry out the actual call to the worker via a broker in order /// to process the request and get the reply which we wait for /// /// <para>it creates a MDPClient and requests from MDPBroker the service. /// if that service exists it uses the returned worker address and issues /// a request with the appropriate data and waits for the reply</para> /// </summary> /// <param name="serviceName">the service's name requested</param> /// <param name="request">request to process by worker</param> /// <param name="serviceClient">mdp client handling the forwarding of requests /// to service offering mdp worker via mdp broker and collecting /// the replies from the mdp worker</param> /// <returns><c>true</c> if successfull and <c>false</c> otherwise</returns> private NetMQMessage ServiceCall(string serviceName, NetMQMessage request, IMDPClient serviceClient = null) { // create MDPClient session and send the request to MDPBroker using (var session = serviceClient ?? new MDPClient(m_titanicAddress)) { session.Timeout = TimeSpan.FromMilliseconds(1000); // 1s session.Retries = 1; // only 1 retry // use MMI protocol to check if service is available var mmi = new NetMQMessage(); // add name of service to inquire mmi.Push(serviceName); // request mmi.service resolution var reply = session.Send("mmi.service", mmi); // first frame should be result of inquiry var rc = reply[0].ConvertToString(); var answer = (MmiCode)Enum.Parse(typeof(MmiCode), rc); // answer == "Ok" -> service is available -> make the request if (answer == MmiCode.Ok) { Log(string.Format("[TITANIC SERVICECALL] -> {0} - {1}", serviceName, request)); return(session.Send(serviceName, request)); } Log(string.Format("[TITANIC SERVICECALL] Service {0} (RC = {1}/{2}) is not available.", serviceName, answer, request)); //! shall this information be available for request? Unknown or Pending //! so TitanicRequest could utilize this information return(null); } }
private void RecreateClient() { var adr = m_client.Address; var id = m_client.Identity; m_client.Dispose(); // recreate the client with the old config values m_client = new MDPClient(adr, id); }
private void EchoClient(IMDPClient client, string serviceName) { var request = new NetMQMessage(); // set the request data request.Push("Helo World!"); // send the request to the service var reply = client.Send(serviceName, request); Assert.That(reply, Is.Not.Null, "[ECHO CLIENT] REPLY was <null>"); Assert.That(reply.FrameCount, Is.EqualTo(1)); Assert.That(reply.First.ConvertToString(), Is.EqualTo("Helo World!")); }
private void AddHelloClient(IMDPClient client, string serviceName) { const string _PAYLOAD = "Hello World"; var request = new NetMQMessage(); // set the request data request.Push(_PAYLOAD); // send the request to the service var reply = client.Send(serviceName, request); Assert.That(reply, Is.Not.Null, "[ADD HELLO CLIENT] REPLY was <null>"); Assert.That(reply.FrameCount, Is.EqualTo(1)); Assert.That(reply.First.ConvertToString(), Is.EqualTo(_PAYLOAD + " - HELLO")); }
private Tuple <NetMQMessage, TitanicReturnCode> ServiceCall(IMDPClient session, TitanicOperation op, NetMQMessage message) { // message can be: // REQUEST: // in goes -> [titanic operation][service requested][request] // returns -> [return code][Guid] // REPLY // in goes -> [titanic operation][request id] // returns -> [return code][reply] // CLOSE // in goes -> [titanic operation][request id] var reply = session.Send(op.ToString(), message); Log(string.Format("received message: {0}", reply)); if (ReferenceEquals(reply, null) || reply.IsEmpty) { return(null); // went wrong - why? I don't care! } // we got a reply -> [return code][data] var rc = reply.Pop().ConvertToString(); // [return code] <- [data] or [service name] if 'Unknown' var status = (TitanicReturnCode)Enum.Parse(typeof(TitanicReturnCode), rc); switch (status) { case TitanicReturnCode.Ok: return(new Tuple <NetMQMessage, TitanicReturnCode> (reply, TitanicReturnCode.Ok)); case TitanicReturnCode.Pending: return(new Tuple <NetMQMessage, TitanicReturnCode> (reply, TitanicReturnCode.Pending)); case TitanicReturnCode.Unknown: Log("ERROR: Service unknown!"); return(new Tuple <NetMQMessage, TitanicReturnCode> (reply, TitanicReturnCode.Unknown)); default: Log("ERROR: FATAL ERROR ABANDONING!"); return(new Tuple <NetMQMessage, TitanicReturnCode> (null, TitanicReturnCode.Failure)); } }
private void MultipleRequestClient(string serviceName, string endpoint, IMDPClient client = null) { const int _NO_OF_RUNS = 100; var reply = new NetMQMessage[_NO_OF_RUNS]; var idC01 = new[] { (byte)'C', (byte)'1' }; client = client ?? new MDPClient(endpoint, idC01); var request = new NetMQMessage(); // set the request data request.Push("Helo World!"); // send the request to the service for (int i = 0; i < _NO_OF_RUNS; i++) { reply[i] = client.Send(serviceName, request); } client.Dispose(); Assert.That(reply, Has.None.Null); Assert.That(reply.All(r => r.First.ConvertToString() == "Helo World!")); }
private void MultipleRequestClient(string serviceName, string endpoint, IMDPClient client = null) { const int _NO_OF_RUNS = 100; var reply = new NetMQMessage[_NO_OF_RUNS]; var idC01 = new[] { (byte)'C', (byte)'1' }; client = client ?? new MDPClient(endpoint, idC01); var request = new NetMQMessage(); // set the request data request.Push("Helo World!"); // send the request to the service for (int i = 0; i < _NO_OF_RUNS; i++) reply[i] = client.Send(serviceName, request); client.Dispose(); Assert.That(reply, Has.None.Null); Assert.That(reply.All(r => r.First.ConvertToString() == "Helo World!")); }
/// <summary> /// ctor - creates a titanic client API object /// for easy access to titanic services /// </summary> /// <param name="client">a valid Major Domo Protocol Client</param> public TitanicClient(IMDPClient client) { m_client = client; }
/// <summary> /// ctor - creates a titanic client API object /// for easy access to titanic services /// </summary> /// <param name="address">a valid NetMQ address string</param> /// <remarks> /// valid NetMQ address string are i.e. /// /// tcp:\\localhost:5555 /// tcp:\\172.12.2.34 /// /// or alike for more information /// <see cref="http://netmq.readthedocs.org/en/latest/transports/"/> /// </remarks> public TitanicClient(string address) { m_client = new MDPClient(address); }
/// <summary> /// carry out the actual call to the worker via a broker in order /// to process the request and get the reply which we wait for /// /// <para>it creates a MDPClient and requests from MDPBroker the service. /// if that service exists it uses the returned worker address and issues /// a request with the appropriate data and waits for the reply</para> /// </summary> /// <param name="serviceName">the service's name requested</param> /// <param name="request">request to process by worker</param> /// <param name="serviceClient">mdp client handling the forwarding of requests /// to service offering mdp worker via mdp broker and collecting /// the replies from the mdp worker</param> /// <returns><c>true</c> if successfull and <c>false</c> otherwise</returns> private NetMQMessage ServiceCall( string serviceName, NetMQMessage request, IMDPClient serviceClient = null) { // create MDPClient session and send the request to MDPBroker using (var session = serviceClient ?? new MDPClient (m_titanicAddress)) { session.Timeout = TimeSpan.FromMilliseconds (1000); // 1s session.Retries = 1; // only 1 retry // use MMI protocol to check if service is available var mmi = new NetMQMessage (); // add name of service to inquire mmi.Push (serviceName); // request mmi.service resolution var reply = session.Send ("mmi.service", mmi); // first frame should be result of inquiry var rc = reply[0].ConvertToString (); var answer = (MmiCode) Enum.Parse (typeof (MmiCode), rc); // answer == "Ok" -> service is available -> make the request if (answer == MmiCode.Ok) { Log (string.Format ("[TITANIC SERVICECALL] -> {0} - {1}", serviceName, request)); return session.Send (serviceName, request); } Log (string.Format ("[TITANIC SERVICECALL] Service {0} (RC = {1}/{2}) is not available.", serviceName, answer, request)); //! shall this information be available for request? Unknown or Pending //! so TitanicRequest could utilize this information return null; } }
/// <summary> /// dispatch the request with the specified GUID th the next available worker if any /// </summary> /// <param name="requestId">request's GUID</param> /// <param name="serviceClient">mdp client to handle mdp broker communication</param> /// <returns><c>true</c> if successfull <c>false</c> otherwise</returns> internal bool DispatchRequests(Guid requestId, IMDPClient serviceClient = null) { // has the request already been processed? -> file does not exist // threat this as successfully processed if (!m_io.ExistsMessage (TitanicOperation.Request, requestId)) { Log (string.Format ("[TITANIC DISPATCH] Request {0} does not exist. Removing it from queue.", requestId)); // close request inorder to avoid any further processing m_io.CloseRequest (requestId); return true; } // load request from file var request = m_io.GetMessage (TitanicOperation.Request, requestId); // [service name][data] var serviceName = request[0].ConvertToString (); Log (string.Format ("[TITANIC DISPATCH] Do a ServiceCall for {0} - {1}.", serviceName, request)); var reply = ServiceCall (serviceName, request, serviceClient); if (ReferenceEquals (reply, null)) return false; // no reply // a reply has been received -> save it Log (string.Format ("[TITANIC DISPATCH] Saving reply for request {0}.", requestId)); // save the reply for further use m_io.SaveMessage (TitanicOperation.Reply, requestId, reply); return true; }
/// <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; } } } }