Lookup: reconnections

This commit is contained in:
2021-09-19 01:30:30 +03:00
parent d0307b47e6
commit be696f17b5
4 changed files with 86 additions and 46 deletions

View File

@ -38,9 +38,12 @@ abstract class Connection
private $onCloseCallback; private $onCloseCallback;
public function __construct( public function __construct(
private string $address, /**
private ClientConfig $clientConfig, * @readonly
private LoggerInterface $logger, */
public string $address,
protected ClientConfig $clientConfig,
protected LoggerInterface $logger,
) { ) {
$this->stream = new NullStream(); $this->stream = new NullStream();
$this->onConnectCallback = static function () { $this->onConnectCallback = static function () {

View File

@ -25,17 +25,23 @@ final class Consumer extends Connection
private $onMessage; private $onMessage;
public function __construct( public function __construct(
private string $address, string $address,
private string $topic, /**
private string $channel, * @readonly
*/
public string $topic,
/**
* @readonly
*/
public string $channel,
callable $onMessage, callable $onMessage,
private ClientConfig $clientConfig, ClientConfig $clientConfig,
private LoggerInterface $logger, LoggerInterface $logger,
) { ) {
parent::__construct( parent::__construct(
$this->address, $address,
$clientConfig, $clientConfig,
$this->logger, $logger,
); );
$this->onMessage = $onMessage; $this->onMessage = $onMessage;

View File

@ -5,6 +5,7 @@ declare(strict_types=1);
namespace Nsq; namespace Nsq;
use Amp\Deferred; use Amp\Deferred;
use Amp\Dns\DnsException;
use Amp\Http\Client\DelegateHttpClient; use Amp\Http\Client\DelegateHttpClient;
use Amp\Http\Client\HttpClientBuilder; use Amp\Http\Client\HttpClientBuilder;
use Amp\Http\Client\Request; use Amp\Http\Client\Request;
@ -123,38 +124,30 @@ final class Lookup
$producers = $this->producers[$topic] ??= new Deferred(); $producers = $this->producers[$topic] ??= new Deferred();
if ($producers instanceof Deferred) { if ($producers instanceof Deferred) {
/** @var array<string, Lookup\Producer> $producers */
$producers = yield $producers->promise(); $producers = yield $producers->promise();
} }
/** @var \Nsq\Lookup\Producer $producer */ foreach (array_diff_key($consumers, $producers) as $address => $producer) {
foreach ($producers as $producer) { unset($consumers[$address]);
$address = $producer->toTcpUri(); }
$consumerKey = $topic.$address;
if (\array_key_exists($consumerKey, $consumers)) { foreach ($producers as $address => $producer) {
if (\array_key_exists($address, $consumers)) {
continue; continue;
} }
$promise = ($consumers[$consumerKey] = new Consumer( $this->keepConnection(
new Consumer(
$address, $address,
$topic, $topic,
$channel, $channel,
$onMessage, $onMessage,
$config, $config,
$this->logger, $this->logger,
))->onClose(function () use ($consumerKey, &$consumers) { ),
unset($consumers[$consumerKey]); $consumers,
})->connect(); );
$promise->onResolve(function (?\Throwable $e) use ($consumerKey, &$consumers) {
if (null !== $e) {
$this->logger->error($e->getMessage());
unset($consumers[$consumerKey]);
}
});
yield $promise;
} }
yield delay($this->config->pollingInterval); yield delay($this->config->pollingInterval);
@ -183,6 +176,45 @@ final class Lookup
$this->logger->info('Unsubscribed.', compact('topic', 'channel')); $this->logger->info('Unsubscribed.', compact('topic', 'channel'));
} }
private function keepConnection(Consumer $consumer, &$consumers): void
{
$consumers[$consumer->address] = $consumer;
asyncCall(function () use ($consumer, &$consumers) {
while (\array_key_exists($consumer->address, $consumers)) {
try {
yield $consumer->connect();
} catch (DnsException $e) {
$this->logger->error($e->getMessage(), ['exception' => $e]);
unset($consumers[$consumer->address], $this->producers[$consumer->topic][$consumer->address]);
return;
} catch (\Throwable $e) {
$this->logger->error($e->getMessage(), ['exception' => $e]);
yield delay($this->config->pollingInterval);
continue;
}
while (true) {
if (!\array_key_exists($consumer->address, $consumers)) {
$consumer->close();
return;
}
if (!$consumer->isConnected()) {
break;
}
yield delay(500);
}
}
});
}
private function watch(string $topic): void private function watch(string $topic): void
{ {
if (\array_key_exists($topic, $this->topicWatchers)) { if (\array_key_exists($topic, $this->topicWatchers)) {
@ -218,15 +250,14 @@ final class Lookup
$producers = []; $producers = [];
foreach ($responses as $response) { foreach ($responses as $response) {
if (($deferred = ($this->producers[$topic] ?? null)) instanceof Deferred) {
$deferred->resolve($response->producers);
unset($this->producers[$topic]);
}
foreach ($response->producers as $producer) { foreach ($response->producers as $producer) {
$producers[$producer->toTcpUri()] = $producer; $producers[$producer->toTcpUri()] = $producer;
} }
} }
if (($deferred = ($this->producers[$topic] ?? null)) instanceof Deferred) {
$deferred->resolve($producers);
}
$this->producers[$topic] = $producers; $this->producers[$topic] = $producers;
yield delay($this->config->pollingInterval); yield delay($this->config->pollingInterval);

View File

@ -16,14 +16,14 @@ use function Amp\call;
final class Producer extends Connection final class Producer extends Connection
{ {
public function __construct( public function __construct(
private string $address, string $address,
private ClientConfig $clientConfig, ClientConfig $clientConfig,
private LoggerInterface $logger, LoggerInterface $logger,
) { ) {
parent::__construct( parent::__construct(
$this->address, $address,
$this->clientConfig, $clientConfig,
$this->logger, $logger,
); );
$context = compact('address'); $context = compact('address');