公司的日志系统、推送系统,都是用的Laravel自带的Queue系统,之前只是调用大佬封装好的,不知道实现的原理,今天仔细看了看Queue的源码,总结一下,有不严谨和错误的地方,还希望大佬们指正。
生产
首先我们的业务用的是Beantalked,所以代码查看从BeanstalkdQueue.php的push入队(later方法为延迟队列)方法开始
/**
* Push a new job onto the queue.
*
* @param string $job
* @param mixed $data
* @param string $queue
* @return mixed
*/
public function push($job, $data = '', $queue = null)
{
return $this->pushRaw($this->createPayload($job, $data), $queue);
}
push方法调用自身类中的pushRaw方法,参数为createPayload方法的返回值,createPayload方法序列化job类作为data,job为Illuminate\Queue\CallQueuedHandler@call(若job为为闭包,则为Illuminate\Queue\Illuminate\Queue\Closure)
/**
* Create a payload string from the given job and data.
*
* @param string $job
* @param mixed $data
* @param string $queue
* @return string
*/
protected function createPayload($job, $data = '', $queue = null)
{
if ($job instanceof Closure)
{
return json_encode($this->createClosurePayload($job, $data));
}
elseif (is_object($job))
{
return json_encode([
'job' => 'Illuminate\Queue\CallQueuedHandler@call',
'data' => ['command' => serialize(clone $job)],
]);
}
return json_encode($this->createPlainPayload($job, $data));
}
/**
* Create a payload string for the given Closure job.
*
* @param \Closure $job
* @param mixed $data
* @return string
*/
protected function createClosurePayload($job, $data)
{
$closure = $this->crypt->encrypt((new Serializer)->serialize($job));
return ['job' => 'IlluminateQueueClosure', 'data' => compact('closure')];
}
/**
* Push a raw payload onto the queue.
*
* @param string $payload
* @param string $queue
* @param array $options
* @return mixed
*/
public function pushRaw($payload, $queue = null, array $options = array())
{
return $this->pheanstalk->useTube($this->getQueue($queue))->put(
$payload, Pheanstalk::DEFAULT_PRIORITY, Pheanstalk::DEFAULT_DELAY, $this->timeToRun
);
}
pushRaw方法中调用了pheanstalk的useTube,pheanstalk为Pheanstalk类,该类为PHP实现的beanstalkd的客户端,useTube方法的参数为getQueue方法的返回值,getQueue方法处理若未传入队列名称,则用默认的default队列
/**
* Get the queue or return the default.
*
* @param string|null $queue
* @return string
*/
public function getQueue($queue)
{
return $queue ?: $this->default;
}
useTube方法设置并选择与队列名相同的tube
/**
* {@inheritdoc}
*/
public function useTube($tube)
{
if ($this->_using != $tube) {
$this->_dispatch(new Command\UseCommand($tube));
$this->_using = $tube;
}
return $this;
}
入队操作,参数和分别为job数据相关、优先级、延迟时间、有效期
/**
* {@inheritdoc}
*/
public function put(
$data,
$priority = PheanstalkInterface::DEFAULT_PRIORITY,
$delay = PheanstalkInterface::DEFAULT_DELAY,
$ttr = PheanstalkInterface::DEFAULT_TTR
)
{
$response = $this->_dispatch(
new Command\PutCommand($data, $priority, $delay, $ttr)
);
return $response['id'];
}
/**
* Puts a job on the queue
* @param string $data The job data
* @param int $priority From 0 (most urgent) to 0xFFFFFFFF (least urgent)
* @param int $delay Seconds to wait before job becomes ready
* @param int $ttr Time To Run: seconds a job can be reserved for
*/
public function __construct($data, $priority, $delay, $ttr)
{
$this->_data = $data;
$this->_priority = $priority;
$this->_delay = $delay;
$this->_ttr = $ttr;
}
消费
消费时调用CallQueuedHandler类中的call方法,call方法从容器中解析job对应的类,并调用setJobInstanceIfNecessary处理,这里是做判断,自己定义的Job类如果use了InteractsWithQueue特性,则调用该特性的setJob方法,注入Illuminate\Contracts\Queue\Job类,从而使自己定义的Job类可以调用删除、重放等等一系列任务相关操作。
/**
* Handle the queued job.
*
* @param \Illuminate\Contracts\Queue\Job $job
* @param array $data
* @return void
*/
public function call(Job $job, array $data)
{
try {
$command = $this->setJobInstanceIfNecessary(
$job, unserialize($data['command'])
);
} catch (ModelNotFoundException $e) {
return $this->handleModelNotFound($job, $e);
}
$this->dispatcher->dispatchNow(
$command, $this->resolveHandler($job, $command)
);
if (! $job->hasFailed() && ! $job->isReleased()) {
$this->ensureNextJobInChainIsDispatched($command);
}
if (! $job->isDeletedOrReleased()) {
$job->delete();
}
}
然后调用call_user_func_array去调用job类中的对应处理方法,第一个参数为数组,元素分别为对应的job和处理method,第二个参数为反序列化后的参数data。