-
Notifications
You must be signed in to change notification settings - Fork 1
/
Scheduler.php
119 lines (107 loc) · 3.54 KB
/
Scheduler.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
<?php
namespace Scheduler;
use Predis\Client;
use Scheduler\Task\SchedulerTask;
use Scheduler\Task\SchedulerTaskInterface;
class Scheduler implements SchedulerInterface {
/**
* @var Client
*/
private $RedisClient;
public function __construct(SchedulerRedisClientFactoryInterface $RedisClientFactory) {
$this->RedisClient = $RedisClientFactory->getRedisClient();
}
/**
* @inheritdoc
*/
public function addOrSet(SchedulerTaskInterface $Task): string {
$this->RedisClient->set($this->getTaskKey($Task->getTaskId()), \msgpack_pack($Task->toArray()));
$this->RedisClient->zadd(
$this->getScheduleKey(),
[$Task->getTaskId() => $Task->getRunTime()]
);
return $Task->getTaskId();
}
/**
* @inheritdoc
*/
public function getAndRemove(int $timestamp, int $count = self::LIMIT): array {
// Ставим блокировку на время выборки и удаления тасков из расписания
$this->lock();
$ScheduleKey = $this->getScheduleKey();
// Выбираем ID тасков из расписания, попадающие в нужное время
$taskIds = $this->RedisClient->zrangebyscore(
$ScheduleKey, 0, $timestamp, [
'limit' => [0, $count],
]
);
$models = [];
if ($taskIds) {
$models = \array_flip($taskIds);
$taskKeys = [];
foreach ($taskIds as $taskId) {
// Собираем ключи для получение данных тасков и удаляем их из расписания
$taskKeys[] = $this->getTaskKey($taskId);
$this->RedisClient->zrem($ScheduleKey, $taskId);
}
$data = $this->RedisClient->mget($taskKeys);
// Подгружаем данные по таскам
foreach ($data as $item) {
$array = (array) \msgpack_unpack($item);
$Task = new SchedulerTask(...\array_values($array));
$models[$Task->getTaskId()] = $Task;
}
// Взятые таски - удаляем
$this->RedisClient->del($taskKeys);
\array_filter($models);
}
// Не забываем снять блокировку
$this->unlock();
return $models;
}
/**
* Установка блокировки
*
* @return bool
*/
private function lock(): bool {
$Key = $this->getLockKey();
if ($this->RedisClient->setnx($Key, 1)) {
$this->RedisClient->expire($Key, 10);
return true;
}
return false;
}
/**
* Снятие блокировки
*/
private function unlock() {
$Key = $this->getLockKey();
$this->RedisClient->del([$Key]);
}
/**
* Возвращает ключ для хранения данных одной таски по её ID
*
* @param string $taskId
* @return string
*/
private function getTaskKey(string $taskId): string {
return 'task:' . $taskId;
}
/**
* Возвращает ключ для лока
*
* @return string
*/
private function getLockKey(): string {
return 'scheduler_lock';
}
/**
* Возвращает ключ для расписания
*
* @return string
*/
private function getScheduleKey(): string {
return 'schedule';
}
}