function client_thread($self) { $context = new ZMQContext(); $client = new ZMQSocket($context, ZMQ::SOCKET_REQ); $endpoint = sprintf("ipc://%s-localfe.ipc", $self); $client->connect($endpoint); $monitor = new ZMQSocket($context, ZMQ::SOCKET_PUSH); $endpoint = sprintf("ipc://%s-monitor.ipc", $self); $monitor->connect($endpoint); $readable = $writeable = array(); while (true) { sleep(mt_rand(0, 4)); $burst = mt_rand(1, 14); while ($burst--) { // Send request with random hex ID $task_id = sprintf("%04X", mt_rand(0, 10000)); $client->send($task_id); // Wait max ten seconds for a reply, then complain $poll = new ZMQPoll(); $poll->add($client, ZMQ::POLL_IN); $events = $poll->poll($readable, $writeable, 10 * 1000000); if ($events > 0) { foreach ($readable as $socket) { $zmsg = new Zmsg($socket); $zmsg->recv(); // Worker is supposed to answer us with our task id assert($zmsg->body() == $task_id); } } else { $monitor->send(sprintf("E: CLIENT EXIT - lost task %s", $task_id)); exit; } } } }
function client_task() { $context = new ZMQContext(); $client = new ZMQSocket($context, ZMQ::SOCKET_DEALER); // Generate printable identity for the client $identity = sprintf("%04X", rand(0, 0x10000)); $client->setSockOpt(ZMQ::SOCKOPT_IDENTITY, $identity); $client->connect("tcp://localhost:5570"); $read = $write = array(); $poll = new ZMQPoll(); $poll->add($client, ZMQ::POLL_IN); $request_nbr = 0; while (true) { // Tick once per second, pulling in arriving messages for ($centitick = 0; $centitick < 100; $centitick++) { $events = $poll->poll($read, $write, 1000); $zmsg = new Zmsg($client); if ($events) { $zmsg->recv(); printf("%s: %s%s", $identity, $zmsg->body(), PHP_EOL); } } $zmsg = new Zmsg($client); $zmsg->body_fmt("request #%d", ++$request_nbr)->send(); } }
/** * Run a self test of the Zmsg class. * * @return boolean * @todo See if assert returns */ public static function test() { $result = true; $context = new ZMQContext(); $output = new ZMQSocket($context, ZMQ::SOCKET_XREQ); $output->bind("inproc://zmsg_selftest"); $input = new ZMQSocket($context, ZMQ::SOCKET_XREP); $input->connect("inproc://zmsg_selftest"); // Test send and receive of single-part message $zmsgo = new Zmsg($output); $zmsgo->body_set("Hello"); $result &= assert($zmsgo->body() == "Hello"); $zmsgo->send(); $zmsgi = new Zmsg($input); $zmsgi->recv(); $result &= assert($zmsgi->parts() == 2); $result &= assert($zmsgi->body() == "Hello"); echo $zmsgi; // Test send and receive of multi-part message $zmsgo = new Zmsg($output); $zmsgo->body_set("Hello"); $zmsgo->wrap("address1", ""); $zmsgo->wrap("address2"); $result &= assert($zmsgo->parts() == 4); echo $zmsgo; $zmsgo->send(); $zmsgi = new Zmsg($input); $zmsgi->recv(); $result &= assert($zmsgi->parts() == 5); $zmsgi->unwrap(); $result &= assert($zmsgi->unwrap() == "address2"); $zmsgi->body_fmt("%s%s", 'W', "orld"); $result &= assert($zmsgi->body() == "World"); // Pull off address 1, check that empty part was dropped $zmsgi->unwrap(); $result &= assert($zmsgi->parts() == 1); // Check that message body was correctly modified $part = $zmsgi->pop(); $result &= assert($part == "World"); $result &= assert($zmsgi->parts() == 0); // Test load and save $zmsg = new Zmsg(); $zmsg->body_set("Hello"); $zmsg->wrap("address1", ""); $zmsg->wrap("address2"); $result &= assert($zmsg->parts() == 4); $fh = fopen(sys_get_temp_dir() . "/zmsgtest.zmsg", 'w'); $zmsg->save($fh); fclose($fh); $fh = fopen(sys_get_temp_dir() . "/zmsgtest.zmsg", 'r'); $zmsg2 = new Zmsg(); $zmsg2->load($fh); assert($zmsg2->last() == $zmsg->last()); fclose($fh); $result &= assert($zmsg2->parts() == 4); echo $result ? "OK" : "FAIL", PHP_EOL; return $result; }
// Get message // - 3-part envelope + content -> request // - 1-part "HEARTBEAT" -> heartbeat $zmsg = new Zmsg($worker); $zmsg->recv(); if ($zmsg->parts() == 3) { // Simulate various problems, after a few cycles $cycles++; if ($cycles > 3 && rand(0, 5) == 0) { printf("I: (%s) simulating a crash%s", $identity, PHP_EOL); break; } elseif ($cycles > 3 && rand(0, 5) == 0) { printf("I: (%s) simulating CPU overload%s", $identity, PHP_EOL); sleep(5); } printf("I: (%s) normal reply - %s%s", $identity, $zmsg->body(), PHP_EOL); $zmsg->send(); $liveness = HEARTBEAT_LIVENESS; sleep(1); // Do some heavy work } elseif ($zmsg->parts() == 1 && $zmsg->body() == 'HEARTBEAT') { $liveness = HEARTBEAT_LIVENESS; } else { printf("E: (%s) invalid message%s%s", $identity, PHP_EOL, $zmsg->__toString()); } $interval = INTERVAL_INIT; } elseif (--$liveness == 0) { printf("W: (%s) heartbeat failure, can't reach queue%s", $identity, PHP_EOL); printf("W: (%s) reconnecting in %d msec...%s", $identity, $interval, PHP_EOL); usleep($interval * 1000); if ($interval < INTERVAL_MAX) {
function main() { for ($client_nbr = 0; $client_nbr < NBR_CLIENTS; $client_nbr++) { $pid = pcntl_fork(); if ($pid == 0) { client_thread(); return; } } for ($worker_nbr = 0; $worker_nbr < NBR_WORKERS; $worker_nbr++) { $pid = pcntl_fork(); if ($pid == 0) { worker_thread(); return; } } $context = new ZMQContext(); $frontend = new ZMQSocket($context, ZMQ::SOCKET_ROUTER); $backend = new ZMQSocket($context, ZMQ::SOCKET_ROUTER); $frontend->bind("ipc://frontend.ipc"); $backend->bind("ipc://backend.ipc"); // 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 $available_workers = 0; $worker_queue = array(); $writeable = $readable = array(); while ($client_nbr > 0) { $poll = new ZMQPoll(); // Poll front-end only if we have available workers if ($available_workers > 0) { $poll->add($frontend, ZMQ::POLL_IN); } // Always poll for worker activity on backend $poll->add($backend, ZMQ::POLL_IN); $events = $poll->poll($readable, $writeable); if ($events > 0) { foreach ($readable as $socket) { // Handle worker activity on backend if ($socket === $backend) { // Queue worker address for LRU routing $zmsg = new Zmsg($socket); $zmsg->recv(); assert($available_workers < NBR_WORKERS); $available_workers++; array_push($worker_queue, $zmsg->unwrap()); if ($zmsg->body() != "READY") { $zmsg->set_socket($frontend)->send(); // exit after all messages relayed $client_nbr--; } } else { if ($socket === $frontend) { $zmsg = new Zmsg($socket); $zmsg->recv(); $zmsg->wrap(array_shift($worker_queue), ""); $zmsg->set_socket($backend)->send(); $available_workers--; } } } } } // Clean up our worker processes foreach ($worker_queue as $worker) { $zmsg = new Zmsg($backend); $zmsg->body_set('END')->wrap($worker, "")->send(); } sleep(1); }
* * @author Ian Barber <ian(dot)barber(at)gmail(dot)com> */ include 'zmsg.php'; $context = new ZMQContext(); $worker = new ZMQSocket($context, ZMQ::SOCKET_REQ); // Set random identity to make tracing easier $identity = sprintf("%04X-%04X", rand(0, 0x10000), rand(0, 0x10000)); $worker->setSockOpt(ZMQ::SOCKOPT_IDENTITY, $identity); $worker->connect("tcp://localhost:5556"); // Tell queue we're ready for work printf("I: (%s) worker ready%s", $identity, PHP_EOL); $worker->send("READY"); $cycles = 0; while (true) { $zmsg = new Zmsg($worker); $zmsg->recv(); $cycles++; // Simulate various problems, after a few cycles if ($cycles > 3 && rand(0, 3) == 0) { printf("I: (%s) simulating a crash%s", $identity, PHP_EOL); break; } elseif ($cycles > 3 && rand(0, 3) == 0) { printf("I: (%s) simulating CPU overload%s", $identity, PHP_EOL); sleep(5); } printf("I: (%s) normal reply - %s%s", $identity, $zmsg->body(), PHP_EOL); sleep(1); // Do some heavy work $zmsg->send(); }