private function initSocket() { $zmq_context = new \ZMQContext(); $this->zmq_socket = $zmq_context->getSocket(\ZMQ::SOCKET_REP); $this->zmq_socket->bind('ipc:///tmp/ebussola-job-schedule.ipc'); chmod('/tmp/ebussola-job-schedule.ipc', 0777); }
/** * @param string $protocol * @param string $encoding * @param bool $synchronous * @param array $endpoint */ public function __construct($protocol, $encoding, $synchronous = false, array $endpoint = []) { parent::__construct($protocol, $encoding, $synchronous, $endpoint); list($type, $dsn, $force, $mode) = array_values($this->endpoint); $this->context = new \ZMQContext(); $this->socket = new \ZMQSocket($this->context, $type); $this->socket->bind($dsn, $force); $this->mode = $mode; }
public function connect() { $context = new \ZMQContext(); $socket = new \ZMQSocket($context, \ZMQ::SOCKET_SUB); $socket->bind($this->dsn()); return $socket; }
function broker_task() { // Prepare our context and sockets $context = new ZMQContext(); $frontend = new ZMQSocket($context, ZMQ::SOCKET_ROUTER); $backend = new ZMQSocket($context, ZMQ::SOCKET_ROUTER); $frontend->bind("tcp://*:5555"); $backend->bind("tcp://*:5556"); // Initialize poll set $poll = new ZMQPoll(); $poll->add($frontend, ZMQ::POLL_IN); $poll->add($backend, ZMQ::POLL_IN); $read = $write = array(); while (true) { $events = $poll->poll($read, $write); foreach ($read as $socket) { $zmsg = new Zmsg($socket); $zmsg->recv(); if ($socket === $frontend) { $zmsg->push("W"); $zmsg->set_socket($backend)->send(); } elseif ($socket === $backend) { $zmsg->pop(); $zmsg->push("C"); $zmsg->set_socket($frontend)->send(); } } } }
public function collect(OutputInterface $output) { $context = new \ZMQContext(); $resultsQueue = new \ZMQSocket($context, \ZMQ::SOCKET_PULL); $resultsQueue->bind(Spider::ZMQ_RESULTS_QUEUE_BIND_DSN); $statusQueue = new \ZMQSocket($context, \ZMQ::SOCKET_PUSH); $statusQueue->bind(Spider::ZMQ_STATUS_QUEUE_BIND_DSN); $tstart = microtime(true); $collectedResults = 0; $expectedResults = PHP_INT_MAX; $output->writeln('Collecting Task results'); while ($collectedResults < $expectedResults) { $string = $resultsQueue->recv(); if ($string === Spider::ZMQ_COMMAND_BATCH_START) { // Wait for start of batch } elseif (stripos($string, Spider::ZMQ_COMMAND_BATCH_END) === false) { $output->writeln('Got task result: ' . substr($string, 0, 20) . ' ...'); file_put_contents($this->resultsTargetPath . '/' . md5($string) . '.result', $string); // TODO: use Symfony/Filesystem $output->writeln('Collected results so far: ' . ++$collectedResults); } else { $expectedResults = (int) explode('%', $string)[1]; $output->writeln('[INFO] Trying to collect ' . $expectedResults . ' as requested by Task Loader'); } } $tend = microtime(true); $totalMsec = ($tend - $tstart) * 1000; $output->writeln('Task results collecting finished. Got ' . $collectedResults . ' results'); $output->writeln("Total elapsed time: {$totalMsec} msec"); $output->writeln('Sending Task Result Collector info'); $statusQueue->send($collectedResults); }
/** * Start the worker and wait for requests */ public function listen() { $context = new \ZMQContext(); $server = new \ZMQSocket($context, \ZMQ::SOCKET_PULL); $server->bind('tcp://127.0.0.1:' . ($this->defaultPort + $this->client->getId() - 1)); $this->logger->info('Client worker ' . $this->client . ' is ready'); while (true) { $request = $server->recv(); $this->logger->debug('Client worker ' . $this->client . ' receiving request : ' . $request); // Check if the input is valid, ignore if wrong $request = json_decode($request, true); if (!$this->isValidInput($request)) { $this->logger->error('Client worker ' . $this->client . ' received an invalid input'); continue; } try { // Call the right method in the client and push to redis the result $result = call_user_func_array(array($this->client, $request['command']), $request['parameters']); } catch (ClientNotReadyException $e) { $this->logger->warning('Client worker ' . $this->client . ' received a request (#' . $request['invokeId'] . ') whereas the client is not ready. This is normal in case of client reconnection process. Ignoring.'); continue; } $key = $this->key . '.client.commands.' . $request['invokeId']; $this->redis->rpush($key, serialize($result)); $this->redis->expire($key, $this->expire); } }
/** * Run ZMQ interface for generator * * Req-rep pattern; msgs are commands: * * GEN = Generate ID * STATUS = Get status string */ public function run() { $context = new \ZMQContext(); $receiver = new \ZMQSocket($context, \ZMQ::SOCKET_REP); $bindTo = 'tcp://*:' . $this->port; echo "Binding to {$bindTo}\n"; $receiver->bind($bindTo); while (TRUE) { $msg = $receiver->recv(); switch ($msg) { case 'GEN': try { $response = $this->generator->generate(); } catch (\Exception $e) { $response = "ERROR"; } break; case 'STATUS': $response = json_encode($this->generator->status()); break; default: $response = 'UNKNOWN COMMAND'; break; } $receiver->send($response); } }
/** * @return void */ protected function init() { $this->logger->info("Running " . self::class . " on " . str_replace("\n", "", `hostname; echo ' - ';uname -a;`)); $this->logger->info("The eviction tick rate is set to {$this->config->getEvictionTicksPerSec()}/second."); // Create the event loop $this->reactLoop = React\EventLoop\Factory::create(); // Object pool $this->queue = new PriorityHashQueue(); // In default mode the latest data will be replaced for a given key. In DATA_MODE_APPEND the data will be appended // internally and available within the consumer as array (for instance for reducing purposes) //$this->queue->setDataMode(PriorityHashQueue::DATA_MODE_APPEND); // Setup ZMQ to send evicted objects. $this->zmqContext = new React\ZMQ\Context($this->reactLoop); $this->logger->info("Binding inbound ZMQ to '{$this->config->getZmqIn()}'."); // Receiver queue for incoming objects $this->zmqInboundQueue = $this->zmqContext->getSocket(\ZMQ::SOCKET_PULL); $this->zmqInboundQueue->bind($this->config->getZmqIn()); $this->logger->info("Binding outbound ZMQ to '{$this->config->getZmqOut()}'."); // Outgoing queue for evicted objects $this->zmqOutboundQueue = $this->zmqContext->getSocket(\ZMQ::SOCKET_PUSH); $this->zmqOutboundQueue->bind($this->config->getZmqOut()); // Register events $this->registerInboundEvents(); $this->registerEvictionEvents(); $this->registerTimedEvents(); }
/** * @param \ZMQContext $context * @return bool */ public function connect(\ZMQContext $context) { $this->clientChannel = new \ZMQSocket($context, ZMQ::SOCKET_ROUTER); $this->workerChannel = new \ZMQSocket($context, ZMQ::SOCKET_ROUTER); try { $this->clientChannel->bind("tcp://*:{$this->config->getClientPort()}"); $this->workerChannel->bind("tcp://*:{$this->config->getWorkerPort()}"); return true; } catch (\ZMQSocketException $e) { return false; } catch (\ZMQException $e) { return false; } catch (\Exception $e) { return false; } }
/** * @param $context * @param $endpoint * @return Zmsg */ private function createPublisher($context, $endpoint) { $publisher = new \ZMQSocket($context, \ZMQ::SOCKET_PUB); $publisher->setSockOpt(\ZMQ::SOCKOPT_SNDHWM, 1); $publisher->setSockOpt(\ZMQ::SOCKOPT_LINGER, 0); $publisher->bind($endpoint); return new Zmsg($publisher); }
/** * @param array[string]string $connUris */ private function initSockets(array $connUris) { // Create context $this->reactLoop = ReactFactory::create(); /** @var ReactZmqContext|\ZMQContext $reactZmqContext */ $reactZmqContext = new ReactZmqContext($this->reactLoop); $this->hbSocket = $reactZmqContext->getSocket(\ZMQ::SOCKET_REP); $this->hbSocket->bind($connUris['hb']); $this->iopubSocket = $reactZmqContext->getSocket(\ZMQ::SOCKET_PUB); $this->iopubSocket->bind($connUris['iopub']); $this->controlSocket = $reactZmqContext->getSocket(\ZMQ::SOCKET_ROUTER); $this->controlSocket->bind($connUris['control']); $this->stdinSocket = $reactZmqContext->getSocket(\ZMQ::SOCKET_ROUTER); $this->stdinSocket->bind($connUris['stdin']); $this->shellSocket = $reactZmqContext->getSocket(\ZMQ::SOCKET_ROUTER); $this->shellSocket->bind($connUris['shell']); }
protected function setupPullSocket($queueId) { $connect = true; if (!empty($this->pull)) { $endpoints = $this->pull->getendpoints(); if (!empty($endpoints["bind"][0]) && $endpoints["bind"][0] != $queueId) { $this->pull->unbind($endpoints["bind"][0]); } else { $connect = false; } } else { $this->pull = $this->socketFactory->createPullSocket(); } if ($connect) { $this->pull->bind($queueId); } }
/** * @param $context * @param $endpoint * @return Zmsg */ private function createCollector($context, $endpoint) { $receiver = new \ZMQSocket($context, \ZMQ::SOCKET_SUB); $receiver->setSockOpt(\ZMQ::SOCKOPT_LINGER, 0); $receiver->setSockOpt(\ZMQ::SOCKOPT_SUBSCRIBE, ""); $receiver->bind($endpoint); return $receiver; }
/** * Binds broker to endpoint * * We use a single socket for both clients and workers. * * @param string $endpoint * @return Broker */ public function bind($endpoint) { $this->endpoint = $endpoint; $this->socket->bind($this->endpoint); if ($this->verbose) { $this->log("ZMQDEBUG", "MDP broker/0.1.1 is active at %s", $this->endpoint); } return $this; }
/** * @return null */ protected function initSockets() { $this->replyToReplyStack = $this->context->getSocket(\ZMQ::SOCKET_REP); $this->replyToReplyStack->bind($this->pulsarSocketsParams->getReplyToReplyStackSocketAddress()); $this->publisher = $this->context->getSocket(\ZMQ::SOCKET_PUB); $this->publisher->bind($this->pulsarSocketsParams->getPublishSocketAddress()); $this->pullActionInfo = $this->context->getSocket(\ZMQ::SOCKET_PULL); $this->pullActionInfo->bind($this->pulsarSocketsParams->getPullSocketAddress()); return null; }
/** * @return null */ public function startCommunication() { $this->initLoop(); $this->context = new \ZMQContext(); $this->pulsarRequestSocket = $this->context->getSocket(\ZMQ::SOCKET_REQ); $this->performersReplySocket = $this->context->getSocket(\ZMQ::SOCKET_REP); $this->initStreams(); $replyStackErrorDtoAlreadySent = false; /** * Receive sockets params from Pulsar and start cyclical communication */ $this->readStream->on(EventsConstants::DATA, function ($data) use($replyStackErrorDtoAlreadySent) { $replyStackDto = null; $replyStackDto = @unserialize($data); if ($replyStackDto !== false && $replyStackDto instanceof ReplyStackDto) { $this->pulsarRequestSocket->connect($replyStackDto->getReplyStackVsPulsarSocketAddress()); $this->performersReplySocket->bind($replyStackDto->getReplyStackVsPerformersSocketAddress()); $this->moduleDto = $replyStackDto; $initDto = new InitStartMethodDto(); $initDto->setShutDownArg('warning'); $this->initStartMethods($initDto); //TODO: make resolver of ways of ReplyStack logging //$this->logger->debug("ReplyStack receive initDto from Pulsar."); $this->loop->nextTick([$this, 'startStackWork']); } else { if ($replyStackErrorDtoAlreadySent === false) { $replyStackErrorDtoAlreadySent = true; $replyStackError = new ReplyStackErrorDto(); $replyStackError->setErrorLevel(ErrorsConstants::CRITICAL); $replyStackError->setErrorReason(PulsarErrorConstants::REPLY_STACK_RECEIVE_NOT_CORRECT_DTO); //write to Pulsar's allotted STDIN about critical error $this->writeStream->write(serialize($replyStackError)); $this->loop->nextTick(function () { $this->loop->stop(); }); } } }); $this->loop->run(); return null; }
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); }
function publisher() { $context = new ZMQContext(); // Prepare publisher $publisher = new ZMQSocket($context, ZMQ::SOCKET_PUB); $publisher->bind("tcp://*:5556"); while (true) { // Send current clock (msecs) to subscribers $publisher->send(microtime(true)); usleep(1000); // 1msec wait } }
public function handlerAction() { try { // setup the ZMQ content so as to avoid issues with conflicts $ctx = new \ZMQContext(); // create a SOCKET_REP server $server = new \ZMQSocket($ctx, \ZMQ::SOCKET_REP); // configure the server socket to not wait at close time // this is intended to minimise the possibility of messages being received and not handled // however as is mentioned in the TODO below they should be handle them explicitly $server->setSockOpt(\ZMQ::SOCKOPT_LINGER, 0); // bind it to tcp on port 5454 $server->bind('tcp://*:5454'); // create a Poll object to enable us to utilize the REQUEST_TIMEOUT functionality $poll = new \ZMQPoll(); $poll->add($server, \ZMQ::POLL_IN); // initialise the read/write buffers for polling $read = $write = array(); // get the time that we start the loop $start = time(); do { // this instruction will wait for a message or the timeout to occur $events = $poll->poll($read, $write, REQUEST_TIMEOUT); // @TODO since exiting the loop will happens after this point a race condition exists // We need to consider solutions that will ensure ALL messages to $server are processed // if the loop will exit after this iteration. // one could check the $events variable as this contains the number of events // however in this situation we only want to process the $read resources and can // just loop through an array (if it is empty nothing will be done) foreach ($read as $socket) { $message = $socket->recv(); $server->send($message . ' World'); } // ensure that even when a message is processed the handler // does not timeout until the REQUEST_TIMEOUT period // has elapsed $active = time() - $start < REQUEST_TIMEOUT / 1000.0; } while ($active); } catch (Exception $e) { // handle the exception // @TODO } // exit the handler die('This handler has timed out'); }
function server_task() { // Launch pool of worker threads, precise number is not critical for ($thread_nbr = 0; $thread_nbr < 5; $thread_nbr++) { $pid = pcntl_fork(); if ($pid == 0) { server_worker(); exit; } } $context = new ZMQContext(); // Frontend socket talks to clients over TCP $frontend = new ZMQSocket($context, ZMQ::SOCKET_ROUTER); $frontend->bind("tcp://*:5570"); // Backend socket talks to workers over ipc $backend = new ZMQSocket($context, ZMQ::SOCKET_DEALER); $backend->bind("ipc://backend"); // Connect backend to frontend via a queue device // We could do this: // $device = new ZMQDevice($frontend, $backend); // But doing it ourselves means we can debug this more easily $read = $write = array(); // Switch messages between frontend and backend while (true) { $poll = new ZMQPoll(); $poll->add($frontend, ZMQ::POLL_IN); $poll->add($backend, ZMQ::POLL_IN); $poll->poll($read, $write); foreach ($read as $socket) { $zmsg = new Zmsg($socket); $zmsg->recv(); if ($socket === $frontend) { //echo "Request from client:"; //echo $zmsg->__toString(); $zmsg->set_socket($backend)->send(); } else { if ($socket === $backend) { //echo "Request from worker:"; //echo $zmsg->__toString(); $zmsg->set_socket($frontend)->send(); } } } } }
/** * Starts an endless running service */ public function start() { $context = new \ZMQContext(); $zmq = new \ZMQSocket($context, \ZMQ::SOCKET_REP); $zmq->bind($this->socket); while (true) { try { $message = '' . $zmq->recv(); $object = json_decode($message, true); $method = $object['method']; $args = $object['params']; $id = $object['id']; $response = $this->handleRequest($id, $method, $args); $zmq->send(json_encode($response)); } catch (\Exception $e) { } } }
protected function declareReplyToPm() { $this->replyToPmSocket = $this->context->getSocket(\ZMQ::SOCKET_REP); $this->replyToPmSocket->bind($this->loadManagerDto->getPmLmSocketsParams()->getPmLmRequestAddress()); $this->replyToPmSocket->on(EventsConstants::ERROR, function (\Exception $e) { $this->logger->error(LoggingExceptions::getExceptionString($e)); }); $this->replyToPmSocket->on(EventsConstants::MESSAGE, function ($receivedDtoContainer) { /** * @var DtoContainer $dtoContainer */ $dtoContainer = unserialize($receivedDtoContainer); $this->processReceivedControlDto($dtoContainer->getDto()); $this->receivePmInfo = true; $this->replyToPmSocket->send(serialize($this->dtoContainer)); }); return null; }
function step2() { $pid = pcntl_fork(); if ($pid == 0) { step1(); exit; } $context = new ZMQContext(); // Bind to ipc: endpoint, then start upstream thread $receiver = new ZMQSocket($context, ZMQ::SOCKET_PAIR); $receiver->bind("ipc://step2.ipc"); // Wait for signal $receiver->recv(); // Signal downstream to step 3 $sender = new ZMQSocket($context, ZMQ::SOCKET_PAIR); $sender->connect("ipc://step3.ipc"); $sender->send(""); }
public function load($inputStream, OutputInterface $output) { $context = new \ZMQContext(); $tasksQueue = new \ZMQSocket($context, \ZMQ::SOCKET_PUSH); $tasksQueue->bind(Spider::ZMQ_TASKS_QUEUE_BIND_DSN); $statusQueue = new \ZMQSocket($context, \ZMQ::SOCKET_PULL); $statusQueue->connect(Spider::ZMQ_STATUS_QUEUE_DSN); /* * http://zguide.zeromq.org/php:all#advanced-request-reply * We have to synchronize the start of the batch with all workers being up and running. * This is a fairly common gotcha in ZeroMQ and there is no easy solution. * The zmq_connect method takes a certain time. * So when a set of workers connect to the ventilator, the first one to successfully connect will get a whole load of messages * in that short time while the others are also connecting. * If you don't synchronize the start of the batch somehow, the system won't run in parallel at all. * Try removing the wait in the ventilator, and see what happens. */ $output->writeln('Giving workers some time to connect'); sleep(3); // The first message is "BATCH_START%" and signals start of batch $tasksQueue->send(Spider::ZMQ_COMMAND_BATCH_START); $taskCount = 0; while (($task = fgets($inputStream)) !== false) { $task = trim(preg_replace('/\\s\\s+/', ' ', $task)); $tasksQueue->send($task); ++$taskCount; } $tasksQueue->send(Spider::ZMQ_COMMAND_BATCH_END . $taskCount); // send info for result collector how many results it should expect $output->writeln("<info>Total count of Tasks put in the Queue: {$taskCount}</info>"); sleep(1); // Give 0MQ time to deliver $output->writeln('Waiting for acknowledgement from Task Result Collector'); $output->writeln('Info from Task Result Collector: ' . $statusQueue->recv()); $output->writeln('<info>Informing all workers to stop</info>'); for ($i = 0; $i < 10; ++$i) { $tasksQueue->send(Spider::ZMQ_COMMAND_WORKER_QUIT); } }
<?php /* * Simple request-reply broker * @author Ian Barber <ian(dot)barber(at)gmail(dot)com> */ // Prepare our context and sockets $context = new ZMQContext(); $frontend = new ZMQSocket($context, ZMQ::SOCKET_ROUTER); $backend = new ZMQSocket($context, ZMQ::SOCKET_DEALER); $frontend->bind("tcp://*:5559"); $backend->bind("tcp://*:5560"); // Initialize poll set $poll = new ZMQPoll(); $poll->add($frontend, ZMQ::POLL_IN); $poll->add($backend, ZMQ::POLL_IN); $readable = $writeable = array(); // Switch messages between sockets while (true) { $events = $poll->poll($readable, $writeable); foreach ($readable as $socket) { if ($socket === $frontend) { // Process all parts of the message while (true) { $message = $socket->recv(); // Multipart detection $more = $socket->getSockOpt(ZMQ::SOCKOPT_RCVMORE); $backend->send($message, $more ? ZMQ::MODE_SNDMORE : null); if (!$more) { break; // Last message part
<?php /* * Lazy Pirate server * Binds REQ socket to tcp://*:5555 * Like hwserver except: * - echoes request as-is * - randomly runs slowly, or exits to simulate a crash. * * @author Ian Barber <ian(dot)barber(at)gmail(dot)com> */ $context = new ZMQContext(); $server = new ZMQSocket($context, ZMQ::SOCKET_REP); $server->bind("tcp://*:5555"); $cycles = 0; while (true) { $request = $server->recv(); $cycles++; // Simulate various problems, after a few cycles if ($cycles > 3 && rand(0, 3) == 0) { echo "I: simulating a crash", PHP_EOL; break; } else { if ($cycles > 3 && rand(0, 3) == 0) { echo "I: simulating CPU overload", PHP_EOL; sleep(5); } } printf("I: normal request (%s)%s", $request, PHP_EOL); sleep(1); // Do some heavy work
<?php /** * Created by PhpStorm. * User: henrygrech-cini * Date: 30/10/2014 * Time: 22:46 */ $ctx = new ZMQContext(); $server = new ZMQSocket($ctx, ZMQ::SOCKET_REP); $server->bind('tcp://*:5454'); while (true) { $message = $server->recv(); $server->send($message . ' World'); }
// Socket to talk to dispatcher $receiver = new ZMQSocket($context, ZMQ::SOCKET_REP); $receiver->connect("ipc://workers.ipc"); while (true) { $string = $receiver->recv(); printf("Received request: [%s]%s", $string, PHP_EOL); // Do some 'work' sleep(1); // Send reply back to client $receiver->send("World"); } } // Launch pool of worker threads for ($thread_nbr = 0; $thread_nbr != 5; $thread_nbr++) { $pid = pcntl_fork(); if ($pid == 0) { worker_routine(); exit; } } // Prepare our context and sockets $context = new ZMQContext(); // Socket to talk to clients $clients = new ZMQSocket($context, ZMQ::SOCKET_ROUTER); $clients->bind("tcp://*:5555"); // Socket to talk to workers $workers = new ZMQSocket($context, ZMQ::SOCKET_DEALER); $workers->bind("ipc://workers.ipc"); // Connect work threads to client threads via a queue $device = new ZMQDevice($clients, $workers); $device->run();
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 $worker_addr = $socket->recv(); assert($available_workers < NBR_WORKERS); $available_workers++; array_push($worker_queue, $worker_addr); // Second frame is empty $empty = $socket->recv(); assert(empty($empty)); // Third frame is READY or else a client reply address $client_addr = $socket->recv(); if ($client_addr != "READY") { $empty = $socket->recv(); assert(empty($empty)); $reply = $socket->recv(); $frontend->send($client_addr, ZMQ::MODE_SNDMORE); $frontend->send("", ZMQ::MODE_SNDMORE); $frontend->send($reply); // exit after all messages relayed $client_nbr--; } } elseif ($socket === $frontend) { // Now get next client request, route to LRU worker // Client request is [address][empty][request] $client_addr = $socket->recv(); $empty = $socket->recv(); assert(empty($empty)); $request = $socket->recv(); $backend->send(array_shift($worker_queue), ZMQ::MODE_SNDMORE); $backend->send("", ZMQ::MODE_SNDMORE); $backend->send($client_addr, ZMQ::MODE_SNDMORE); $backend->send("", ZMQ::MODE_SNDMORE); $backend->send($request); $available_workers--; } } } } // Clean up our worker processes foreach ($worker_queue as $worker) { $backend->send($worker, ZMQ::MODE_SNDMORE); $backend->send("", ZMQ::MODE_SNDMORE); $backend->send('END'); } sleep(1); }
$endpoint = sprintf("ipc://%s-cloud.ipc", $self); $cloudfe->setSockOpt(ZMQ::SOCKOPT_IDENTITY, $self); $cloudfe->bind($endpoint); // Connect cloud backend to all peers $cloudbe = $context->getSocket(ZMQ::SOCKET_XREP); $cloudbe->setSockOpt(ZMQ::SOCKOPT_IDENTITY, $self); for ($argn = 2; $argn < $_SERVER['argc']; $argn++) { $peer = $_SERVER['argv'][$argn]; printf("I: connecting to cloud backend at '%s'%s", $peer, PHP_EOL); $endpoint = sprintf("ipc://%s-cloud.ipc", $peer); $cloudbe->connect($endpoint); } // Bind state backend / publisher to endpoint $statebe = new ZMQSocket($context, ZMQ::SOCKET_PUB); $endpoint = sprintf("ipc://%s-state.ipc", $self); $statebe->bind($endpoint); // Connect statefe to all peers $statefe = $context->getSocket(ZMQ::SOCKET_SUB); $statefe->setSockOpt(ZMQ::SOCKOPT_SUBSCRIBE, ""); for ($argn = 2; $argn < $_SERVER['argc']; $argn++) { $peer = $_SERVER['argv'][$argn]; printf("I: connecting to state backend at '%s'%s", $peer, PHP_EOL); $endpoint = sprintf("ipc://%s-state.ipc", $peer); $statefe->connect($endpoint); } // Prepare monitor socket $monitor = new ZMQSocket($context, ZMQ::SOCKET_PULL); $endpoint = sprintf("ipc://%s-monitor.ipc", $self); $monitor->bind($endpoint); // Prepare local frontend and backend $localfe = new ZMQSocket($context, ZMQ::SOCKET_XREP);