Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 37950de

Browse files
committed
redid
1 parent 4f03854 commit 37950de

10 files changed

+332
-121
lines changed
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Enqueue\Redis;
6+
7+
class RedisBlockingConsumeStrategy implements RedisConsumeStrategy
8+
{
9+
use RedisConsumerHelperTrait;
10+
11+
/**
12+
* @var string[]
13+
*/
14+
private $queueNames;
15+
16+
/**
17+
* @var RedisContext
18+
*/
19+
private $context;
20+
21+
public function __construct(RedisContext $context)
22+
{
23+
$this->context = $context;
24+
}
25+
26+
/**
27+
* @param RedisDestination[] $queues
28+
* @param int $timeout
29+
* @param int $redeliveryDelay
30+
*
31+
* @return RedisMessage|null
32+
*/
33+
public function receiveMessage(array $queues, int $timeout, int $redeliveryDelay): ?RedisMessage
34+
{
35+
$startAt = time();
36+
$thisTimeout = (int) ceil($timeout / 1000);
37+
38+
if (null === $this->queueNames) {
39+
$this->queueNames = [];
40+
foreach ($queues as $queue) {
41+
$this->queueNames[] = $queue->getName();
42+
}
43+
}
44+
45+
while ($thisTimeout > 0) {
46+
$this->migrateExpiredMessages($this->context->getRedis(), $this->queueNames);
47+
48+
if (false == $result = $this->context->getRedis()->brpop($this->queueNames, $thisTimeout)) {
49+
return null;
50+
}
51+
52+
$this->pushQueueNameBack($this->queueNames, $result->getKey());
53+
54+
if ($message = $this->processResult($result, $redeliveryDelay)) {
55+
return $message;
56+
}
57+
58+
$thisTimeout -= time() - $startAt;
59+
}
60+
61+
return null;
62+
}
63+
64+
public function receiveMessageNoWait(RedisDestination $queue, int $redeliveryDelay): ?RedisMessage
65+
{
66+
$this->migrateExpiredMessages($this->context->getRedis(), [$queue]);
67+
68+
if ($result = $this->context->getRedis()->rpop($queue->getName())) {
69+
return $this->processResult($result, $redeliveryDelay);
70+
}
71+
72+
return null;
73+
}
74+
75+
public function resetState()
76+
{
77+
$this->queueNames = null;
78+
}
79+
80+
protected function processResult(RedisResult $result, int $redeliveryDelay): ?RedisMessage
81+
{
82+
$message = $this->context->getSerializer()->toMessage($result->getMessage());
83+
84+
$now = time();
85+
86+
if (0 === $message->getAttempts() && $expiresAt = $message->getHeader('expires_at')) {
87+
if ($now > $expiresAt) {
88+
return null;
89+
}
90+
}
91+
92+
$message->setHeader('attempts', $message->getAttempts() + 1);
93+
$message->setRedelivered($message->getAttempts() > 1);
94+
$message->setKey($result->getKey());
95+
$message->setReservedKey($this->context->getSerializer()->toString($message));
96+
97+
$reservedQueue = $result->getKey().':reserved';
98+
$redeliveryAt = $now + $redeliveryDelay;
99+
100+
$this->context->getRedis()->zadd($reservedQueue, $message->getReservedKey(), $redeliveryAt);
101+
102+
return $message;
103+
}
104+
}

pkg/redis/RedisConnectionFactory.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010

1111
class RedisConnectionFactory implements ConnectionFactory
1212
{
13+
const CONSUME_STRATEGY_BLOCKING = 'blocking';
14+
const CONSUME_STRATEGY_NON_BLOCKING = 'non_blocking';
15+
1316
/**
1417
* @var array
1518
*/
@@ -87,7 +90,7 @@ public function createContext(): Context
8790
if ($this->config['lazy']) {
8891
return new RedisContext(function () {
8992
return $this->createRedis();
90-
}, $this->config['redelivery_delay']);
93+
}, $this->config);
9194
}
9295

9396
return new RedisContext($this->createRedis(), $this->config['redelivery_delay']);
@@ -161,6 +164,7 @@ private function defaultConfig(): array
161164
'predis_options' => null,
162165
'ssl' => null,
163166
'redelivery_delay' => 300,
167+
'consume_strategy' => self::CONSUME_STRATEGY_BLOCKING,
164168
];
165169
}
166170
}

pkg/redis/RedisConsumeStrategy.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Enqueue\Redis;
6+
7+
interface RedisConsumeStrategy
8+
{
9+
public function receiveMessage(array $queues, int $timeout, int $redeliveryDelay): ?RedisMessage;
10+
11+
public function receiveMessageNoWait(RedisDestination $queue, int $redeliveryDelay): ?RedisMessage;
12+
13+
public function resetState();
14+
}

pkg/redis/RedisConsumer.php

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@
1111

1212
class RedisConsumer implements Consumer
1313
{
14-
use RedisConsumerHelperTrait;
15-
1614
/**
1715
* @var RedisDestination
1816
*/
@@ -23,15 +21,24 @@ class RedisConsumer implements Consumer
2321
*/
2422
private $context;
2523

24+
/**
25+
* @var RedisConsumeStrategy
26+
*/
27+
private $consumeStrategy;
28+
2629
/**
2730
* @var int
2831
*/
2932
private $redeliveryDelay = 300;
3033

31-
public function __construct(RedisContext $context, RedisDestination $queue)
32-
{
34+
public function __construct(
35+
RedisContext $context,
36+
RedisDestination $queue,
37+
RedisConsumeStrategy $consumeStrategy
38+
) {
3339
$this->context = $context;
3440
$this->queue = $queue;
41+
$this->consumeStrategy = $consumeStrategy;
3542
}
3643

3744
/**
@@ -63,8 +70,6 @@ public function getQueue(): Queue
6370
*/
6471
public function receive(int $timeout = 0): ?Message
6572
{
66-
$timeout = (int) ceil($timeout / 1000);
67-
6873
if ($timeout <= 0) {
6974
while (true) {
7075
if ($message = $this->receive(5000)) {
@@ -73,15 +78,15 @@ public function receive(int $timeout = 0): ?Message
7378
}
7479
}
7580

76-
return $this->receiveMessage([$this->queue], $timeout, $this->redeliveryDelay);
81+
return $this->consumeStrategy->receiveMessage([$this->queue], $timeout, $this->redeliveryDelay);
7782
}
7883

7984
/**
8085
* @return RedisMessage
8186
*/
8287
public function receiveNoWait(): ?Message
8388
{
84-
return $this->receiveMessageNoWait($this->queue, $this->redeliveryDelay);
89+
return $this->consumeStrategy->receiveMessageNoWait($this->queue, $this->redeliveryDelay);
8590
}
8691

8792
/**

pkg/redis/RedisConsumerHelperTrait.php

Lines changed: 9 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -6,113 +6,29 @@
66

77
trait RedisConsumerHelperTrait
88
{
9-
/**
10-
* @var string[]
11-
*/
12-
protected $queueNames;
13-
14-
abstract protected function getContext(): RedisContext;
15-
16-
/**
17-
* @param RedisDestination[] $queues
18-
* @param int $timeout
19-
* @param int $redeliveryDelay
20-
*
21-
* @return RedisMessage|null
22-
*/
23-
protected function receiveMessage(array $queues, int $timeout, int $redeliveryDelay): ?RedisMessage
24-
{
25-
$startAt = time();
26-
$thisTimeout = $timeout;
27-
28-
if (null === $this->queueNames) {
29-
$this->queueNames = [];
30-
foreach ($queues as $queue) {
31-
$this->queueNames[] = $queue->getName();
32-
}
33-
}
34-
35-
while ($thisTimeout > 0) {
36-
$this->migrateExpiredMessages($this->queueNames);
37-
38-
if (false == $result = $this->getContext()->getRedis()->brpop($this->queueNames, $thisTimeout)) {
39-
return null;
40-
}
41-
42-
$this->pushQueueNameBack($result->getKey());
43-
44-
if ($message = $this->processResult($result, $redeliveryDelay)) {
45-
return $message;
46-
}
47-
48-
$thisTimeout -= time() - $startAt;
49-
}
50-
51-
return null;
52-
}
53-
54-
protected function receiveMessageNoWait(RedisDestination $destination, int $redeliveryDelay): ?RedisMessage
55-
{
56-
$this->migrateExpiredMessages([$destination->getName()]);
57-
58-
if ($result = $this->getContext()->getRedis()->rpop($destination->getName())) {
59-
return $this->processResult($result, $redeliveryDelay);
60-
}
61-
62-
return null;
63-
}
64-
65-
protected function processResult(RedisResult $result, int $redeliveryDelay): ?RedisMessage
66-
{
67-
$message = $this->getContext()->getSerializer()->toMessage($result->getMessage());
68-
69-
$now = time();
70-
71-
if (0 === $message->getAttempts() && $expiresAt = $message->getHeader('expires_at')) {
72-
if ($now > $expiresAt) {
73-
return null;
74-
}
75-
}
76-
77-
$message->setHeader('attempts', $message->getAttempts() + 1);
78-
$message->setRedelivered($message->getAttempts() > 1);
79-
$message->setKey($result->getKey());
80-
$message->setReservedKey($this->getContext()->getSerializer()->toString($message));
81-
82-
$reservedQueue = $result->getKey().':reserved';
83-
$redeliveryAt = $now + $redeliveryDelay;
84-
85-
$this->getContext()->getRedis()->zadd($reservedQueue, $message->getReservedKey(), $redeliveryAt);
86-
87-
return $message;
88-
}
89-
90-
protected function pushQueueNameBack(string $queueName): void
9+
protected function pushQueueNameBack(array &$queueNames, string $queueName): void
9110
{
92-
if (count($this->queueNames) <= 1) {
11+
if (count($queueNames) <= 1) {
9312
return;
9413
}
9514

96-
if (false === $from = array_search($queueName, $this->queueNames, true)) {
15+
if (false === $from = array_search($queueName, $queueNames, true)) {
9716
throw new \LogicException(sprintf('Queue name was not found: "%s"', $queueName));
9817
}
9918

100-
$to = count($this->queueNames) - 1;
19+
$to = count($queueNames) - 1;
10120

102-
$out = array_splice($this->queueNames, $from, 1);
103-
array_splice($this->queueNames, $to, 0, $out);
21+
$out = array_splice($queueNames, $from, 1);
22+
array_splice($queueNames, $to, 0, $out);
10423
}
10524

106-
protected function migrateExpiredMessages(array $queueNames): void
25+
protected function migrateExpiredMessages(Redis $redis, array $queueNames): void
10726
{
10827
$now = time();
10928

11029
foreach ($queueNames as $queueName) {
111-
$this->getContext()->getRedis()
112-
->eval(LuaScripts::migrateExpired(), [$queueName.':delayed', $queueName], [$now]);
113-
114-
$this->getContext()->getRedis()
115-
->eval(LuaScripts::migrateExpired(), [$queueName.':reserved', $queueName], [$now]);
30+
$redis->eval(LuaScripts::migrateExpired(), [$queueName.':delayed', $queueName], [$now]);
31+
$redis->eval(LuaScripts::migrateExpired(), [$queueName.':reserved', $queueName], [$now]);
11632
}
11733
}
11834
}

0 commit comments

Comments
 (0)