예제 #1
1
 function enqueue($fid, $prioridad = 10)
 {
     // Carga biblioteca
     $this->_inicializa_pheanstalk();
     // Guardar en BBDD
     $result = $this->store($fid, self::PENDING, '');
     if ($result === FALSE) {
         return FALSE;
     }
     // Encolar en beanstalkd
     $host = $this->config->item('beanstalkd_host');
     $port = $this->config->item('beanstalkd_port');
     $tube = $this->config->item('beanstalkd_tube');
     $exito = FALSE;
     try {
         $pheanstalk = new Pheanstalk($host, $port);
         $pheanstalk->useTube($tube)->put("SCAN " . $fid, $prioridad);
         $exito = TRUE;
     } catch (Exception $e) {
         // TODO : más detalle
         log_message('error', 'Error encolando trabajo en beanstalkd');
         $this->store($fid, self::ERROR, 'El fichero no se pudo encolar para su revisión');
         /* En este caso, no es mala idea que el fichero quede
          * en la base de datos marcado con ERROR, pero que la ejecución
          * siga
          */
     }
     if ($exito === TRUE) {
         log_message('info', 'Fichero ' . $fid . ' encolado para ser escaneado');
     }
 }
예제 #2
0
 /**
  * @throws Exception\WorkerException
  */
 public function testWorkerRuns()
 {
     $testWorkerRuns = $this;
     $tube = 'worker_tube_' . rand(53, 504);
     $data = 'worker_value_' . rand(95, 3000);
     $pheanstalk = new Pheanstalk(self::SERVER_HOST, self::SERVER_PORT);
     $worker = new Worker(self::SERVER_HOST, self::SERVER_PORT);
     $job = $pheanstalk->useTube($tube)->put($data);
     $worker->register($tube, function (Job $job) use($testWorkerRuns, $data) {
         $testWorkerRuns->assertEquals($data, $job->getData());
     });
     $processedJob = $worker->processOne(0);
     $stats = $pheanstalk->statsTube($tube);
     $this->assertEquals($stats['total-jobs'], 1);
 }
예제 #3
0
 private function _createPheanstalk()
 {
     $pheanstalk = new Pheanstalk(self::SERVER_HOST);
     $tube = preg_replace('#[^a-z]#', '', strtolower(__CLASS__));
     $pheanstalk->useTube($tube)->watch($tube)->ignore('default');
     try {
         while ($pheanstalk->delete($pheanstalk->peekDelayed())) {
         }
     } catch (Pheanstalk_Exception_ServerException $e) {
     }
     try {
         while ($pheanstalk->delete($pheanstalk->peekReady())) {
         }
     } catch (Pheanstalk_Exception_ServerException $e) {
     }
     return $pheanstalk;
 }
예제 #4
0
        /**
         * dispatchWork
         * 
         * @param string $function
         * @param array $parameters
         * @param object $socket
         */
        protected function dispatchWork($f,$p) {
                $this->Log->debug(__METHOD__);
                
                // If this work is to be done locally set it up right here
                if('local'==$this->work['method']) {
                        
                        // dispatch the API call
                        include_once($_SERVER['SCRIPT_FILENAME']);
                        $this->Log->debug('include_once('.$_SERVER['SCRIPT_FILENAME'].')');

                        // The expected class name
                        $class_name = ANYAPI_CALL_CLASS;

                        if(!class_exists($class_name)) {
                                $this->Log->error("Code 531: Unable to locate the expected class named '".$class_name."' in ".$_SERVER['SCRIPT_FILENAME']);
                                $response['error'] = array('code'=>'531','message'=>'Internal system error');
                                $this->out($response); // terminates
                        }
                        
                        // Try and instantiate the class
                        try {
                                $anyapi = new $class_name;
                        } catch (Exception $e) {
                                $this->Log->error("Code 540: Unable instantiate class named '".$class_name."' got exception ".$e);
                                $response['error'] = array('code'=>'540','message'=>'Internal system error');
                                $this->out($response); // terminates
                        }

                        // There must be a better way to do this - uck!!
                        if(0==count($p))     { return $anyapi->$f(); }
                        elseif(1==count($p)) { return $anyapi->$f($p[0]); }
                        elseif(2==count($p)) { return $anyapi->$f($p[0],$p[1]); }
                        elseif(3==count($p)) { return $anyapi->$f($p[0],$p[1],$p[2]); }
                        elseif(4==count($p)) { return $anyapi->$f($p[0],$p[1],$p[2],$p[3]); }
                        elseif(5==count($p)) { return $anyapi->$f($p[0],$p[1],$p[2],$p[3],$p[4]); }
                        elseif(6==count($p)) { return $anyapi->$f($p[0],$p[1],$p[2],$p[3],$p[4],$p[5]); }
                        elseif(7==count($p)) { return $anyapi->$f($p[0],$p[1],$p[2],$p[3],$p[4],$p[5],$p[6]); }
                        elseif(8==count($p)) { return $anyapi->$f($p[0],$p[1],$p[2],$p[3],$p[4],$p[5],$p[6],$p[7]); }
                        elseif(9==count($p)) { return $anyapi->$f($p[0],$p[1],$p[2],$p[3],$p[4],$p[5],$p[6],$p[7],$p[8]); }
                        elseif(10==count($p)){ return $anyapi->$f($p[0],$p[1],$p[2],$p[3],$p[4],$p[5],$p[6],$p[7],$p[8],$p[9]); }
                        elseif(11==count($p)){ return $anyapi->$f($p[0],$p[1],$p[2],$p[3],$p[4],$p[5],$p[6],$p[7],$p[8],$p[9],$p[10]); }
                        elseif(12==count($p)){ return $anyapi->$f($p[0],$p[1],$p[2],$p[3],$p[4],$p[5],$p[6],$p[7],$p[8],$p[9],$p[10],$p[11]); }
                        elseif(13==count($p)){ return $anyapi->$f($p[0],$p[1],$p[2],$p[3],$p[4],$p[5],$p[6],$p[7],$p[8],$p[9],$p[10],$p[11],$p[12]); }
                        elseif(14==count($p)){ return $anyapi->$f($p[0],$p[1],$p[2],$p[3],$p[4],$p[5],$p[6],$p[7],$p[8],$p[9],$p[10],$p[11],$p[12],$p[13]); }
                        elseif(15==count($p)){ return $anyapi->$f($p[0],$p[1],$p[2],$p[3],$p[4],$p[5],$p[6],$p[7],$p[8],$p[9],$p[10],$p[11],$p[12],$p[13],$p[14]); }

                        $this->Log->error("Code 563: AnyApi only supports 15x input parameters for an action - got an elegant fix?");
                        $response['error'] = array('code'=>'563','message'=>'Internal system error');
                        
                        return $response;
                }
                
                // Check we are setup for beanstalkd since it's the only type supported at the moment
                if('beanstalkd' != $this->work['queue']['type']) {
                        $this->Log->error("Code 565: AnyApi only supports work[queue][type] = 'beanstalkd'");
                        $response['error'] = array('code'=>'565','message'=>'Internal system error');
                        return $response;
                }
                
                $socket = null;

                // Only create a return data socket if the worker will not be doing an external callback
                if(!isset($this->request['return_options']['callback']['url']) && !isset($this->request['return_options']['callback']['email'])) {

                        $this->Log->debug("Creating an internet tcp stream socket for work return data");
                        $socket = socket_create(AF_INET,SOCK_STREAM,getprotobyname('tcp')); 

                        // Make sure we created the socket
                        if(!$socket) {
                                $this->Log->error("Code 490: Unable to create work queue return data socket!");
                                $response['error'] = array('code'=>'490','message'=>'Internal system error');
                                $this->out($response); // terminates
                        }
                        
                        // Bind the socket to something we can use
                        $return_token = false;
                        while(!$return_token) {

                                $port_delta = $this->work['queue']['callback_socket']['max_port'] - $this->work['queue']['callback_socket']['min_port'];
                                $port = $this->work['queue']['callback_socket']['min_port'] + rand(0,$port_delta);

                                if(@socket_bind($socket,$this->work['queue']['callback_socket']['address'],$port)) {
                                        $this->Log->debug('Bound socket to '.$this->work['queue']['callback_socket']['address'].':'.$port.' ready for work queue return data');
                                        $return_token = md5(mt_rand(0,10000000).microtime(true));
                                }
                        }
                        
                        $this->request['return_options']['callback']['socket'] = array(
                            'address' =>$this->work['queue']['callback_socket']['address'],
                            'port' => $port,
                            'token' => $return_token,
                            'timeout' => $this->work['queue']['callback_socket']['timeout']
                        );
                }
                
                // Beanstalk!
                include_once(dirname(__FILE__).'/pheanstalk/pheanstalk_init.php');
                $Pheanstalk = new Pheanstalk($this->work['queue']['host'],$this->work['queue']['port'],$this->work['queue']['connect_timeout']);
                
                $job_id = null;
                try {
                        $job_id = $Pheanstalk
                                ->useTube(ANYAPI_CALL_CLASS) // use the class name for the tube name
                                ->put(gzcompress(serialize(array($f,$p,$this->request['return_options'])),9));
                        
                } catch (Exception $e) {
                        $this->Log->error("Code 589: Unable to place work onto beanstalkd queue at ".$this->work['queue']['host'].':'.$this->work['queue']['port']);
                        $response['error'] = array('code'=>'589','message'=>'Internal system error');
                        return $response;
                }
                $this->Log->debug("Work placed on beanstalkd queue with job_id:".$job_id);
                
                // Wait for callback response from the worker into the socket here
                socket_listen($socket);
                $this->Log->debug("Socket listening for response data for job_id:".$job_id);
                
                socket_set_nonblock($socket);
                $this->Log->debug("Socket set to non-blocking mode for job_id:".$job_id);
                
                // Wait until timeout for the callback socket response
                $timeout_timestamp = time() + $this->work['queue']['callback_socket']['timeout'];
                $usleep_interval = 100; // 1/10000th of a second
                
                $connection = false;
                while(!$connection) {
                        
                        $connection = @socket_accept($socket);
                        
                        usleep($usleep_interval);
                        $usleep_interval = $usleep_interval*1.1; // gradual scaling back of $usleep_interval
                        
                        if(time() > $timeout_timestamp) {
                                $this->Log->error('Code 456: Timed out after while for response data from job_id: '.$job_id);
                                $response['error'] = array('code'=>'456','message'=>'Internal system error');
                                return $response;
                        }
                }
                
                // Slurp up the data in a buffered manner
                $data = null;
                do {
                        @socket_recv($connection, $buffer,1024,MSG_DONTWAIT);
                        
                        // Protect against resource temporarily unavailable problems
                        if(11 == socket_last_error($connection)) {
                                socket_clear_error($connection);
                                $buffer = 'CONTINUE';
                        } else {
                                $data .= $buffer;
                        }
                        
                } while (!empty($buffer));
                
                // Uncompress the data returned through the socket
                $data = @gzuncompress($data);
                
                // Strip apart data and token componets
                $token = substr($data,(strlen($data)-strlen($this->request['return_options']['callback']['socket']['token'])),strlen($this->request['return_options']['callback']['socket']['token']));
                $data = substr($data,0,strlen($data)-strlen($this->request['return_options']['callback']['socket']['token'])-1 );
                
                // Make sure the token matches the expected value
                if($token != $this->request['return_options']['callback']['socket']['token']) {
                        $this->Log->error('Code 472: Invalid return data token for job_id: '.$job_id);
                        $response['error'] = array('code'=>'472','message'=>'Internal system error');
                        return $response;
                }
                
                // Detect if this is raw binary data or not as indicated by a BIN\0 at the front of the string
                if("BIN\0" == substr($data,0,4)) {
                        return substr($data,4);
                }
                return json_decode($data,true);
        }
 /**
  * Put the error on a beanstalk queue so a separate process can
  * send it off to Airbrake via HTTP. Requires the pheanstalk client.
  * Config:
  * $BEANSTALK_SERVERS[] = array(
  *    'host' => 'example.com',
  *    'port' => '11300'
  * );
  * @author Aaron Parecki
  */
 public function beanstalkRequest($url, $headers, $body)
 {
     global $BEANSTALK_SERVERS;
     require_once 'pheanstalk/pheanstalk_init.php';
     $k = array_rand($BEANSTALK_SERVERS);
     $pheanstalk = new Pheanstalk($BEANSTALK_SERVERS[$k]['host'], $BEANSTALK_SERVERS[$k]['port']);
     $pheanstalk->useTube('airbrake');
     $pheanstalk->put(json_encode(array('url' => $url, 'headers' => $headers, 'body' => $body)));
 }