Beispiel #1
0
 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);
 }
Beispiel #2
0
 /**
  * Send request to broker and get reply by hook or crook.
  *
  * Takes ownership of request message and destroys it when sent.
  *
  *
  * @param   string  $service  The name of the service
  * @param   Zmsg    $request  Request message
  * @return  Zmsg    Returns the reply message or NULL if there was no reply.
  */
 public function send($service, Zmsg $request)
 {
     // Prefix request with protocol frames
     // Frame 1: "MDPCxy" (six bytes, MDP/Client)
     // Frame 2: Service name (printable string)
     $request->push($service);
     $request->push(Mdp::CLIENT);
     if ($this->verbose) {
         $this->log("ZMQDEBUG", "send request to '%s' service:\n--\n%s", $service, (string) $request);
     }
     $retries_left = $this->retries;
     $read = $write = array();
     while ($retries_left) {
         $request->setSocket($this->client)->send();
         // Poll socket for a reply, with timeout
         $poll = new ZMQPoll();
         $poll->add($this->client, ZMQ::POLL_IN);
         $events = $poll->poll($read, $write, $this->timeout);
         // If we got a reply, process it
         if ($events) {
             $request->recv();
             if ($this->verbose) {
                 $this->log("ZMQDEBUG", "received reply:\n--\n%s", $request);
             }
             if ($request->parts() < 3) {
                 throw new MdpException(sprintf("Expected more than 2 parts, but %d received", $request->parts()));
             }
             $header = $request->pop();
             if ($header !== Mdp::CLIENT) {
                 throw new MdpException(sprintf("Unexpected header %s, %s is expected", $header, Mdp::CLIENT));
             }
             $replyService = $request->pop();
             if ($replyService != $service) {
                 throw new MdpException(sprintf("Unexpected service %s, %s is expected.", $replyService, $service));
             }
             //Success
             return $request;
         } elseif ($retries_left--) {
             if ($this->verbose) {
                 $this->log("WARN", "no reply, reconnecting...");
             }
             // Reconnect
             $this->connect();
             // Resend message again
             $request->send();
         } else {
             if ($this->verbose) {
                 $this->log("ERROR", "permanent error, abandoning request");
                 break;
             }
         }
     }
 }
Beispiel #3
0
 /**
  * Receives a reply
  *
  * Returns the reply message or NULL if there was no reply.
  * Does not attempt to recover from a broker failure, this is not possible
  * without storing all unanswered requests and resending them all ...
  */
 public function recv()
 {
     $read = $write = array();
     // Poll socket for a reply, with timeout
     $poll = new ZMQPoll();
     $poll->add($this->client, ZMQ::POLL_IN);
     $events = $poll->poll($read, $write, $this->timeout);
     // If we got a reply, process it
     if ($events) {
         $msg = new Zmsg($this->client);
         $msg->recv();
         if ($this->verbose) {
             $this->log('ZMQDEBUG', "received reply:\n--\n%s", (string) $msg);
         }
         if ($msg->parts() < 4) {
             throw new MdpException("More than 3 parts are expected in the message.");
         }
         $msg->pop();
         $header = $msg->pop();
         if ($header !== Mdp::CLIENT) {
             throw new MdpException(sprintf("%s is expected.", Mdp::CLIENT));
         }
         $repService = $msg->pop();
         return $msg;
     } else {
         if ($this->verbose) {
             $this->log('WARN', "permanent error, abandoning request");
         }
         return;
     }
 }
Beispiel #4
0
 /**
  * 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();
         }
     }
 }
Beispiel #5
0
 /**
  * This is the main listen and process loop
  */
 public function listen()
 {
     $read = $write = array();
     // Get and process messages forever or until interrupted
     while (true) {
         $poll = new ZMQPoll();
         $poll->add($this->socket, ZMQ::POLL_IN);
         $events = $poll->poll($read, $write, $this->heartbeat);
         // Process next input message, if any
         if ($events) {
             $zmsg = new Zmsg($this->socket);
             $zmsg->recv();
             if ($this->verbose) {
                 $this->log("ZMQDEBUG", "received message:\n--\n%s", (string) $zmsg);
             }
             $sender = $zmsg->pop();
             $empty = $zmsg->pop();
             $header = $zmsg->pop();
             if ($header == Mdp::CLIENT) {
                 $this->processClient($sender, $zmsg);
             } elseif ($header == Mdp::WORKER) {
                 $this->processWorker($sender, $zmsg);
             } else {
                 if ($this->verbose) {
                     $this->log("ERROR", "invalid message\n--\n%s", (string) $zmsg);
                 }
             }
         }
         // Disconnect and delete any expired workers
         // Send heartbeats to idle workers if needed
         if (microtime(true) > $this->heartbeatAt) {
             $this->purgeWorkers();
             foreach ($this->workers as $worker) {
                 $this->workerSend($worker, Mdp::WORKER_HEARTBEAT);
             }
             $this->updateHeartbeatExpiry();
         }
     }
 }