本文目录导读:

在 PHP 中实现“公平队列”,通常指的是按优先级或按资源占用比例来调度任务,确保高优先级任务不会无限等待,同时低优先级任务也能得到执行机会。
PHP 本身没有内置“公平队列”类,但可以通过以下几种方式实现:
基于优先级的 SplPriorityQueue
PHP 内置的 SplPriorityQueue 可以实现优先队列,但默认是“绝对优先级”模式(高优先级永远先执行),要让它“公平”,需要调整优先级计算逻辑。
<?php
class FairPriorityQueue extends SplPriorityQueue
{
// 公平性:记录每个优先级最后执行的时间,动态调整优先级
private array $lastExecutedTime = [];
private int $basePriority;
public function __construct(int $basePriority = 100)
{
$this->basePriority = $basePriority;
}
public function insert(mixed $value, mixed $priority): void
{
// 存储原始优先级和插入时间
parent::insert([
'value' => $value,
'priority' => $priority,
'insert_time' => microtime(true)
], $priority);
}
public function extract(): mixed
{
$data = parent::extract();
$priority = $data['priority'];
// 记录执行时间
$this->lastExecutedTime[$priority] = microtime(true);
return $data['value'];
}
// 公平调度:根据等待时间和优先级动态计算
public function compare($priority1, $priority2): int
{
// 动态优先级 = 原始优先级 + 等待时间补偿
$waitTime1 = microtime(true) - $this->lastExecutedTime[$priority1] ?? 0;
$waitTime2 = microtime(true) - $this->lastExecutedTime[$priority2] ?? 0;
// 等待时间越长,等效优先级越高
$effectiveP1 = $priority1 + ($waitTime1 * 10); // 权重可调
$effectiveP2 = $priority2 + ($waitTime2 * 10);
return $effectiveP1 <=> $effectiveP2;
}
}
// 使用示例
$queue = new FairPriorityQueue();
$queue->insert('low_task', 1); // 低优先级
$queue->insert('high_task', 10); // 高优先级
$queue->insert('mid_task', 5); // 中优先级
while ($queue->count() > 0) {
echo $queue->extract() . "\n";
// 高优先级执行后,低优先级会逐渐提升等效优先级
}
基于时间片的轮询队列
适合需要公平分配 CPU 或资源使用量的场景:
<?php
class TimeSliceFairQueue
{
private array $queues = []; // 按优先级分组
private array $timeSlice = []; // 每个优先级的时间片(秒)
private int $currentPriority = 0;
private float $sliceStartTime;
public function __construct(array $timeSlices = [1 => 0.1, 2 => 0.05])
{
$this->timeSlice = $timeSlices;
$this->sliceStartTime = microtime(true);
}
public function enqueue($item, int $priority = 1): void
{
if (!isset($this->queues[$priority])) {
$this->queues[$priority] = new SplQueue();
}
$this->queues[$priority]->enqueue($item);
}
public function dequeue(): mixed
{
if (empty($this->queues)) return null;
// 检查当前优先级是否超时
$elapsed = microtime(true) - $this->sliceStartTime;
if ($elapsed > ($this->timeSlice[$this->currentPriority] ?? 0.1)) {
$this->rotatePriority();
}
// 从当前优先级队列获取任务
$queue = &$this->queues[$this->currentPriority];
if ($queue && !$queue->isEmpty()) {
$item = $queue->dequeue();
if ($queue->isEmpty()) {
unset($this->queues[$this->currentPriority]);
$this->rotatePriority();
}
return $item;
}
// 如果当前队列为空,切换到下一个
$this->rotatePriority();
return $this->dequeue();
}
private function rotatePriority(): void
{
$priorities = array_keys($this->queues);
if (empty($priorities)) {
$this->currentPriority = 0;
return;
}
// 轮转到下一个优先级
$currentIndex = array_search($this->currentPriority, $priorities);
$nextIndex = ($currentIndex === false) ? 0 : ($currentIndex + 1) % count($priorities);
$this->currentPriority = $priorities[$nextIndex];
$this->sliceStartTime = microtime(true);
}
}
使用 Redis 实现分布式公平队列
对于分布式系统,可以使用 Redis 的有序集合(Sorted Set)实现公平队列:
<?php
class RedisFairQueue
{
private \Redis $redis;
private string $queueKey;
public function __construct(\Redis $redis, string $queueKey = 'fair_queue')
{
$this->redis = $redis;
$this->queueKey = $queueKey;
}
// 添加任务,priority 越高越优先
public function push(string $task, int $priority = 0): void
{
// 使用时间戳保证公平性:优先级 + 时间戳微调
$score = ($priority * 1000000) + (PHP_INT_MAX - microtime(true) * 1000000);
$this->redis->zAdd($this->queueKey, $score, $task);
}
// 取出最高优先级的任务
public function pop(): ?string
{
$tasks = $this->redis->zRange($this->queueKey, 0, 0);
if (empty($tasks)) return null;
$task = $tasks[0];
$this->redis->zRem($this->queueKey, $task);
return $task;
}
// 公平取出(防止饥饿)
public function fairPop(): ?string
{
// 获取所有任务及其分数
$tasks = $this->redis->zRange($this->queueKey, 0, -1, true);
if (empty($tasks)) return null;
// 计算每个任务的等待时间(基于插入时间)
$now = microtime(true) * 1000000;
$bestTask = null;
$bestScore = PHP_FLOAT_MAX;
foreach ($tasks as $task => $score) {
$priority = floor($score / 1000000);
$insertTime = PHP_INT_MAX - ($score % 1000000);
$waitTime = $now - $insertTime;
// 公平得分 = 优先级权重 + 等待时间权重
$fairScore = 1 / ($priority + 1) + ($waitTime / 1000000) * 0.1;
if ($fairScore < $bestScore) {
$bestScore = $fairScore;
$bestTask = $task;
}
}
if ($bestTask) {
$this->redis->zRem($this->queueKey, $bestTask);
}
return $bestTask;
}
}
使用 Laravel 的队列系统实现公平
如果你使用 Laravel,可以利用其队列系统的优先级和延迟功能:
// 1. 定义不同的队列名称
dispatch(new HighPriorityJob())->onQueue('high');
dispatch(new LowPriorityJob())->onQueue('low');
// 2. 配置 worker 按比例消费
// 在 supervisor 配置中设置不同权重
// [program:worker-high]
// command=php artisan queue:work --queue=high,low --sleep=3 --tries=3
// numprocs=3 # 高优先级分配更多进程
// 3. 或者使用队列的延迟平衡策略
dispatch((new Job())->delay(now()->addMinutes(5))); // 低优先级任务延迟执行
选择建议
| 场景 | 推荐方案 |
|---|---|
| 单进程内存队列 | SplPriorityQueue + 动态优先级 |
| 资源公平分配 | 时间片轮询队列 |
| 分布式系统 | Redis 有序集合 + 公平评分 |
| Laravel 项目 | 多队列 + 进程分配 |
关键设计原则
- 防止饥饿:确保低优先级任务最终也能获得执行机会
- 动态权重:根据等待时间调整优先级
- 可配置:让用户能调整公平性参数(如时间片大小、权重系数)
- 监控:记录每个优先级的等待时间和执行次数
实现公平队列的核心在于平衡不同优先级任务的执行机会,而不是严格保证 FIFO 顺序,根据你的具体业务需求选择合适的实现方式。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播