Exemplo n.º 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);
 }
Exemplo n.º 2
0
 /**
  * Makes sure the broker is running.
  * If it isn't running method will start it.
  *
  * @return int  Returns non false if broker is running
  */
 public static function ensureBrokerRunning()
 {
     $client = (new Client(\Scalr::config('scalr.crontab.sockets.broker')))->setTimeout(100)->setRetries(1)->setLogger(\Scalr::getContainer()->logger('Mdp\\Client')->setLevel(\Scalr::config('scalr.crontab.log_level')))->connect();
     $mmiReq = new Zmsg();
     $mmiReq->push("system.healthcheck");
     $mmiRep = $client->send("mmi.service", $mmiReq);
     if ($mmiRep) {
         $ok = $mmiRep->pop();
     } else {
         $ok = false;
         //Make sure another broker process isn't hanging
         self::terminateBroker();
         //Broker has to be started in the separate process
         $op = [];
         $logFile = \Scalr::config('scalr.crontab.log');
         exec(self::getStartBrokerCommand() . ' ' . ($logFile == '/dev/null' ? '> ' : '>> ') . escapeshellcmd($logFile) . ' 2>&1 & echo $!', $op);
         $pid = intval($op[0]);
     }
     return $ok;
 }
Exemplo n.º 3
0
        try {
            $strRequestPayload = @json_encode($payload->body);
            $payload->setBody($task->worker($payload->body));
            $payload->code = 200;
        } catch (Exception $e) {
            $task->log('ERROR', "Worker %s failed with exception:%s - %s", $task->getName(), get_class($e), $e->getMessage());
            $payload = $payload->error(500, $e->getMessage());
        }
        $executionTime = microtime(true) - $start;
        $statPath = '/var/log/scalr/worker.log';
        if (is_writable($statPath)) {
            @error_log(sprintf("%s,%d,\"%s\",%0.4f,%d,\"%s\"\n", date('M d H:i:s P'), isset($payload->code) ? $payload->code : 500, $service, $executionTime, \Scalr::getDb()->numberQueries + (\Scalr::getContainer()->analytics->enabled ? \Scalr::getContainer()->cadb->numberQueries : 0), str_replace('"', '""', $strRequestPayload)), 3, $statPath);
        }
        //Resets the number of the queries
        \Scalr::getDb()->numberQueries = 0;
        if (\Scalr::getContainer()->analytics->enabled) {
            \Scalr::getContainer()->cadb->numberQueries = 0;
        }
    }
    //It checks memory usage for demonized tasks
    if ($config->daemon && !$task->checkMemoryUsage()) {
        //Adds the pid of the process to payload to handle it on client's side
        $payload->dw = posix_getpid();
        //It does not even exit execution loop. Client should start a replacement in its time.
        //We cannot start worker from here because it won't be correctly terminated by client.
    }
    $reply = new Zmsg();
    $reply->setLast(serialize($payload));
    unset($payload);
    unset($request);
}
Exemplo n.º 4
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;
             }
         }
     }
 }
Exemplo n.º 5
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;
     }
 }
Exemplo n.º 6
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();
         }
     }
 }
Exemplo n.º 7
0
 /**
  * Process a request coming from a client
  *
  * @param   string $sender The address of the sender
  * @param   Zmsg   $msg    A message
  */
 public function processClient($sender, Zmsg $msg)
 {
     $serviceFrame = $msg->pop();
     $service = $this->fetchService($serviceFrame);
     // Set reply return address to client sender
     $msg->wrap($sender, "");
     if (substr($serviceFrame, 0, 4) == 'mmi.') {
         $this->handleInternalService($serviceFrame, $msg);
     } else {
         $this->dispatchService($service, $msg);
     }
 }
Exemplo n.º 8
0
 /**
  * Runs ZMQ MDP Asynchronous Client
  *
  * @throws Exception
  */
 protected function launchClient()
 {
     $this->launchWorkers();
     //We don't even need to start client if queue is empty
     if ($this->queue->count() == 0) {
         $this->log('DEBUG', "It does not need to start major-domo client as queue is empty.");
         return;
     }
     $this->log('DEBUG', "Launching %s 0mq mdp client", $this->name);
     $session = (new AsynClient(\Scalr::config('scalr.crontab.sockets.broker'), true))->setLogger(\Scalr::getContainer()->logger('Mdp\\AsynClient')->setLevel(\Scalr::config('scalr.crontab.log_level')))->setTimeout(\Scalr::config('scalr.crontab.heartbeat.delay') * \Scalr::config('scalr.crontab.heartbeat.liveness') * 2)->connect();
     $this->log('DEBUG', 'Sending request messages to broker');
     $payloadClass = $this->payloadClass;
     //The number of the requests sent
     $count = 0;
     //Array of the messages which are sent
     $sentMessages = [];
     //Gets queue iterator in order to gracefully iterate over an ArrayObject removing each offset
     $it = $this->queue->getIterator();
     //Sending messages loop
     while ($it->valid()) {
         $key = $it->key();
         //Creates standard payload for zmq messaging
         $payload = (new $payloadClass($it->current()))->setId();
         $sentMessages[$payload->getId()] = $payload;
         $request = new Zmsg();
         $request->setLast(serialize($payload));
         //Sends the message to worker
         if ($payload instanceof PayloadRouterInterface) {
             $session->send($payload->getAddress($this), $request);
         } else {
             $session->send($this->name, $request);
         }
         $count++;
         $it->next();
         //Removing the message from the queue
         $it->offsetUnset($key);
     }
     //Cleanup queue
     unset($this->queue);
     $this->log('DEBUG', 'Polling results');
     //Receiving loop
     for ($i = 0; $i < $count; $i++) {
         $msg = $session->recv();
         if (!$msg) {
             // Interrupt or failure
             $this->getLogger()->fatal("Some worker failed!");
             break;
         }
         //We are having deal with serialized data
         $payload = @unserialize($msg->getLast());
         if (!$payload instanceof AbstractPayload) {
             throw new TaskException(sprintf("Unexpected reply from worker: '%s'.", $msg->getLast()));
         }
         //Checks if worker reaches a memory limit
         if (!empty($payload->dw)) {
             $this->toDisconnect[$payload->dw] = $payload instanceof PayloadRouterInterface ? $payload->getAddress($this) : $this->name;
             $this->log("DEBUG", "Client got PID:%d from the worker %s to disconnect", $payload->dw, $this->toDisconnect[$payload->dw]);
         }
         if (!isset($sentMessages[$payload->getId()])) {
             //Message comes from previous session?
             $this->getLogger()->warn("Strange message came from another session. Payload:%s", var_export($payload, true));
             $count++;
         } else {
             //We get response so remove record
             unset($sentMessages[$payload->getId()]);
             //Triggers onResponse callback
             $this->onResponse($payload);
         }
     }
     if (!empty($this->toDisconnect) && $this->config()->daemon) {
         foreach ($this->toDisconnect as $pid => $address) {
             //Terminates worker
             $this->terminateWorker($pid);
             //We need to get up a replacement for that one
             $pid = $this->addWorker($address);
             //It is important to save a PID of the process to be able terminate all workers along with client
             $this->pids[$pid] = $pid;
         }
         //Resets event
         $this->toDisconnect = [];
         usleep(100000);
     }
     $this->onCompleted();
 }