public static void Main(string[] args) { var workers = new List<Thread>(); var clients = new List<Thread>(); // Prepare our context and sockets using (var ctx = ZmqContext.Create()) { using (ZmqSocket frontend = ctx.CreateSocket(SocketType.ROUTER), backend = ctx.CreateSocket(SocketType.ROUTER)) { frontend.Bind("tcp://*:5555"); backend.Bind("tcp://*:5556"); int clientId; for (clientId = 0; clientId < Program.clients; clientId++) { clients.Add(new Thread(ClientTask)); clients[clientId].Start(); } for (int workerId = 0; workerId < Program.workers; workerId++) { workers.Add(new Thread(WorkerTask)); workers[workerId].Start(); } // Logic of LRU loop // - Poll backend always, frontend only if 1+ worker ready // - If worker replies, queue worker as ready and forward reply // to client if necessary // - If client requests, pop next worker and send request to it // Queue of available workers var workerQueue = new Queue<byte[]>(); // Handle worker activity on backend backend.ReceiveReady += (socket, revents) => { var zmsg = new ZMessage(revents.Socket); // Use worker address for LRU routing workerQueue.Enqueue(zmsg.Unwrap()); // Forward message to client if it's not a READY if (!Encoding.Unicode.GetString(zmsg.Address).Equals("READY")) { zmsg.Send(frontend); } }; frontend.ReceiveReady += (socket, revents) => { // Now get next client request, route to next worker // Dequeue and drop the next worker address var zmsg = new ZMessage(revents.Socket); zmsg.Wrap(workerQueue.Dequeue(), new byte[0]); zmsg.Send(backend); }; var poller = new Poller(new List<ZmqSocket>{ frontend, backend }); while (true) { //int rc = context.Poller(workerQueue.Count > 0 // ? new List<ZmqSocket>(new ZmqSocket[] {frontend, backend}) // : new List<ZmqSocket>(new ZmqSocket[] {backend})); int rc = poller.Poll(); if (rc == -1) { break; } } } } }
static void Main(string[] args) { using (var context = ZmqContext.Create()) { int interval = INTERVAL_INIT; int liveness = HEARTBEAT_LIVENESS; while (true) { int cylces = 0; using (ZmqSocket worker = ConnectWorker(context)) { worker.ReceiveReady += (socket, revents) => { var zmsg = new ZMessage(revents.Socket); byte[] identity = zmsg.Unwrap(); switch (Encoding.Unicode.GetString(zmsg.Address)) { case PPP_HEARTBEAT: interval = INTERVAL_INIT; liveness = HEARTBEAT_LIVENESS; break; default: if (zmsg.FrameCount > 1) { if (!doTheWork(cylces++)) { break; } interval = INTERVAL_INIT; liveness = HEARTBEAT_LIVENESS; Console.WriteLine("W: work completed"); zmsg.Send(worker); } else { Console.WriteLine("E: invalied message {0}", identity); } break; }; }; //Send out heartbeats at regular intervals DateTime heartbeat_at = DateTime.Now.AddMilliseconds(HEARTBEAT_INTERVAL); while (true) { var poller = new Poller(new List<ZmqSocket> { worker }); poller.Poll(TimeSpan.FromMilliseconds(HEARTBEAT_INTERVAL * 1000)); //If liveness hits zero, queue is considered disconnected if (--liveness <= 0) { Console.WriteLine("W: heartbeat failure, can't reach queue."); Console.WriteLine("W: reconnecting in {0} msecs...", interval); try { Thread.Sleep(interval); } catch (System.Exception ex) { Console.WriteLine(ex.Message); } //Exponential Backoff if (interval < INTERVAL_MAX) interval *= 2; liveness = HEARTBEAT_LIVENESS; //Break the while loop and start the connection over break; } //Send heartbeat to queue if it's time if (DateTime.Now > heartbeat_at) { heartbeat_at = DateTime.Now.AddMilliseconds(HEARTBEAT_INTERVAL); ZMessage zmsg = new ZMessage(PPP_HEARTBEAT); zmsg.Send(worker); } } } } } }