public function testComplex() { $context = new \ZMQContext(); $output = new \ZMQSocket($context, \ZMQ::SOCKET_DEALER); $output->bind("inproc://zmsg_selftest"); $input = new \ZMQSocket($context, \ZMQ::SOCKET_ROUTER); $input->connect("inproc://zmsg_selftest"); // Test send and receive of single-part message $zmsgo = new Zmsg($output); $zmsgo->setLast("Hello"); $this->assertTrue($zmsgo->getLast() == "Hello"); $zmsgo->send(); $zmsgi = new Zmsg($input); $zmsgi->recv(); $this->assertTrue($zmsgi->parts() == 2); $this->assertTrue($zmsgi->getLast() == "Hello"); // Test send and receive of multi-part message $zmsgo = new Zmsg($output); $zmsgo->setLast("Hello"); $zmsgo->wrap("address1", ""); $zmsgo->wrap("address2"); $this->assertTrue($zmsgo->parts() == 4); $zmsgo->send(); $zmsgi = new Zmsg($input); $zmsgi->recv(); $this->assertTrue($zmsgi->parts() == 5); $zmsgi->unwrap(); $this->assertTrue($zmsgi->unwrap() == "address2"); $zmsgi->setLast(sprintf("%s%s", 'W', "orld")); $this->assertTrue($zmsgi->getLast() == "World"); // Pull off address 1, check that empty part was dropped $zmsgi->unwrap(); $this->assertTrue($zmsgi->parts() == 1); // Check that message body was correctly modified $part = $zmsgi->pop(); $this->assertTrue($part == "World"); $this->assertTrue($zmsgi->parts() == 0); // Test load and save $zmsg = new Zmsg(); $zmsg->setLast("Hello"); $zmsg->wrap("address1", ""); $zmsg->wrap("address2"); $this->assertTrue($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->getLast() == $zmsg->getLast()); fclose($fh); $this->assertTrue($zmsg2->parts() == 4); }
/** * Send reply, if any, to broker and wait for next request. * * @param Zmsg $reply optional Reply message object * @return Zmsg Returns if there is a request to process */ public function recv($reply = null) { // Format and send the reply if we were provided one if (!$reply && $this->expectReply) { throw new MdpException("Reply message is expected"); } if ($reply) { $reply->wrap($this->replyTo); $this->send(Mdp::WORKER_REPLY, null, $reply); } $this->expectReply = true; $read = $write = []; while (true) { $poll = new ZMQPoll(); $poll->add($this->worker, ZMQ::POLL_IN); $events = $poll->poll($read, $write, $this->heartbeat); if ($events) { $zmsg = new Zmsg($this->worker); $zmsg->recv(); if ($this->verbose) { $this->log("ZMQDEBUG", "received message from broker:\n--\n%s", (string) $zmsg); } $this->liveness = self::HEARTBEAT_LIVENESS; if ($zmsg->parts() < 3) { throw new MdpException(sprintf("Expected more then 2 parts, but %d received", $zmsg->parts())); } $zmsg->pop(); $header = $zmsg->pop(); if ($header !== Mdp::WORKER) { throw new MdpException(sprintf("Expected %s header, %s has been actually received", Mdp::WORKER, $header)); } $command = $zmsg->pop(); if ($command == Mdp::WORKER_REQUEST) { // We should pop and save as many addresses as there are // up to a null part, but for now, just save one… $this->replyTo = $zmsg->unwrap(); // We have a request to process return $zmsg; } elseif ($command == Mdp::WORKER_HEARTBEAT) { // Do nothing for heartbeats } elseif ($command == Mdp::WORKER_DISCONNECT) { $this->connect(); } else { if ($this->verbose) { $this->log("ERROR", "invalid input message\n--\n%s", (string) $zmsg); } } } elseif (--$this->liveness == 0) { // poll ended on timeout, $event being false if ($this->verbose) { $this->log("WARN", "disconnected from broker - retrying...\n"); } usleep($this->reconnect * 1000); $this->connect(); } // Send HEARTBEAT if it's time if (microtime(true) > $this->heartbeatAt) { $this->send(Mdp::WORKER_HEARTBEAT); $this->updateHeartbeatExpiry(); } } }
/** * Process message sent to us by a worker * * @param string $sender The address of the worker * @param \Scalr\System\Zmq\Zmsg $msg The message */ public function processWorker($sender, $msg) { $command = $msg->pop(); $workerReady = isset($this->workers[$sender]); $worker = $this->fetchWorker($sender); if ($command == Mdp::WORKER_READY) { if ($workerReady) { // Not first command in session $this->deleteWorker($worker, true); } elseif (strlen($sender) >= 4 && substr($sender, 0, 4) == 'mmi.') { // Reserved service name $this->deleteWorker($worker, true); } else { // Attach worker to service and mark as idle $serviceFrame = $msg->pop(); $worker->service = $this->fetchService($serviceFrame); $worker->service->workers++; $this->waitWorker($worker); } } elseif ($command == Mdp::WORKER_REPLY) { if ($workerReady) { // Remove & save client return envelope and insert the // protocol header and service name, then rewrap envelope. $client = $msg->unwrap(); $msg->push($worker->service->name); $msg->push(Mdp::CLIENT); $msg->wrap($client, ""); $msg->setSocket($this->socket)->send(); if ($this->verbose) { $this->log("ZMQDEBUG", "worker replied:\n--\n%s", (string) $msg); } $this->waitWorker($worker); } else { $this->deleteWorker($worker, true); } } elseif ($command == Mdp::WORKER_HEARTBEAT) { if ($workerReady) { $worker->expiry = microtime(true) + $this->heartbeatExpiry / 1000; } else { $this->deleteWorker($worker, true); } } elseif ($command == Mdp::WORKER_DISCONNECT) { $this->deleteWorker($worker, true); if ($this->verbose) { $this->log("ZMQDEBUG", "disconnect worker\n--\n%s", (string) $msg); } } else { if ($this->verbose) { $this->log("ERROR", "invalid input message\n--\n%s", (string) $msg); } } }