2020-10-09 16:34:33 +08:00
|
|
|
<?php
|
2020-10-29 18:17:25 +08:00
|
|
|
declare(strict_types=1);
|
2020-10-09 16:34:33 +08:00
|
|
|
|
|
|
|
|
namespace Kafka;
|
|
|
|
|
|
|
|
|
|
|
2020-10-26 16:51:03 +08:00
|
|
|
use RdKafka\Conf;
|
2020-10-29 18:17:25 +08:00
|
|
|
use RdKafka\Consumer;
|
2020-10-28 11:40:33 +08:00
|
|
|
use RdKafka\ConsumerTopic;
|
2020-10-28 15:54:09 +08:00
|
|
|
use RdKafka\Exception;
|
2020-10-26 16:51:03 +08:00
|
|
|
use RdKafka\KafkaConsumer;
|
2020-10-27 18:46:22 +08:00
|
|
|
use RdKafka\TopicConf;
|
2020-10-09 16:34:33 +08:00
|
|
|
use Snowflake\Snowflake;
|
2020-10-10 11:30:10 +08:00
|
|
|
use Swoole\Coroutine\Channel;
|
2020-10-09 16:34:33 +08:00
|
|
|
use Swoole\Process;
|
2020-10-29 18:17:25 +08:00
|
|
|
use Throwable;
|
2020-10-09 16:34:33 +08:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Class Queue
|
|
|
|
|
* @package Queue
|
|
|
|
|
*/
|
|
|
|
|
class Kafka extends \Snowflake\Process\Process
|
|
|
|
|
{
|
|
|
|
|
|
2020-10-28 11:42:01 +08:00
|
|
|
protected Channel $channel;
|
2020-10-26 17:08:52 +08:00
|
|
|
|
2021-02-20 15:32:58 +08:00
|
|
|
private int $maxLength = 5000;
|
|
|
|
|
|
2020-10-26 17:08:52 +08:00
|
|
|
|
2020-10-10 11:30:10 +08:00
|
|
|
/**
|
|
|
|
|
* @param Process $process
|
2020-10-26 17:02:38 +08:00
|
|
|
* @throws \Exception
|
2020-10-10 11:30:10 +08:00
|
|
|
*/
|
2021-01-05 15:24:30 +08:00
|
|
|
public function onHandler(Process $process): void
|
2020-10-10 11:30:10 +08:00
|
|
|
{
|
2021-03-26 01:18:11 +08:00
|
|
|
$this->waite(swoole_unserialize($process->read()));
|
2020-10-10 11:30:10 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2020-10-28 11:40:33 +08:00
|
|
|
/**
|
|
|
|
|
* @param array $kafkaServer
|
2021-03-09 19:40:10 +08:00
|
|
|
* @throws \Exception
|
2020-10-28 11:40:33 +08:00
|
|
|
*/
|
|
|
|
|
private function waite(array $kafkaServer)
|
|
|
|
|
{
|
2020-12-28 17:33:52 +08:00
|
|
|
try {
|
2021-03-29 11:36:09 +08:00
|
|
|
name($this->pid, 'Kafka Consumer ' . $kafkaServer['topic']);
|
2021-02-14 21:36:22 +08:00
|
|
|
|
2020-12-28 17:33:52 +08:00
|
|
|
[$config, $topic, $conf] = $this->kafkaConfig($kafkaServer);
|
2021-01-14 10:41:16 +08:00
|
|
|
if (empty($config) && empty($topic) && empty($conf)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2020-12-28 17:33:52 +08:00
|
|
|
$objRdKafka = new Consumer($config);
|
|
|
|
|
$topic = $objRdKafka->newTopic($kafkaServer['topic'], $topic);
|
2020-12-28 17:31:51 +08:00
|
|
|
|
2021-01-12 17:31:51 +08:00
|
|
|
$topic->consumeStart(0, RD_KAFKA_OFFSET_STORED);
|
|
|
|
|
do {
|
2020-12-28 17:33:52 +08:00
|
|
|
$this->resolve($topic, $conf['interval'] ?? 1000);
|
2021-01-12 17:31:51 +08:00
|
|
|
} while (true);
|
2020-12-28 17:33:52 +08:00
|
|
|
} catch (Throwable $exception) {
|
2021-04-01 10:47:28 +08:00
|
|
|
logger()->addError($exception, 'throwable');
|
2020-10-28 11:55:11 +08:00
|
|
|
}
|
2020-10-28 11:46:43 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param ConsumerTopic $topic
|
2020-10-28 11:55:30 +08:00
|
|
|
* @param $interval
|
2021-03-16 15:46:07 +08:00
|
|
|
* @throws \Exception
|
2020-10-28 11:46:43 +08:00
|
|
|
*/
|
2020-10-28 11:55:30 +08:00
|
|
|
private function resolve(ConsumerTopic $topic, $interval)
|
2020-10-28 11:46:43 +08:00
|
|
|
{
|
|
|
|
|
try {
|
2020-10-28 11:55:30 +08:00
|
|
|
$message = $topic->consume(0, $interval);
|
2020-10-28 11:46:43 +08:00
|
|
|
if (empty($message)) {
|
|
|
|
|
return;
|
2020-10-28 11:40:33 +08:00
|
|
|
}
|
2020-10-28 11:46:43 +08:00
|
|
|
if ($message->err == RD_KAFKA_RESP_ERR_NO_ERROR) {
|
2021-02-20 18:33:17 +08:00
|
|
|
$this->handlerExecute($message->topic_name, $message);
|
2020-10-28 11:46:43 +08:00
|
|
|
} else if ($message->err == RD_KAFKA_RESP_ERR__PARTITION_EOF) {
|
2021-01-13 10:35:26 +08:00
|
|
|
$this->application->warning('No more messages; will wait for more');
|
2020-10-28 11:46:43 +08:00
|
|
|
} else if ($message->err == RD_KAFKA_RESP_ERR__TIMED_OUT) {
|
|
|
|
|
$this->application->error('Kafka Timed out');
|
|
|
|
|
} else {
|
|
|
|
|
$this->application->error($message->errstr());
|
|
|
|
|
}
|
2020-10-29 18:17:25 +08:00
|
|
|
} catch (Throwable $exception) {
|
2021-04-01 10:47:28 +08:00
|
|
|
logger()->addError($exception, 'throwable');
|
2020-10-28 11:46:43 +08:00
|
|
|
}
|
2020-10-28 11:40:33 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2020-10-10 13:55:14 +08:00
|
|
|
/**
|
|
|
|
|
* @param $topic
|
|
|
|
|
* @param $message
|
2021-03-16 15:46:07 +08:00
|
|
|
* @throws \Exception
|
2020-10-10 13:55:14 +08:00
|
|
|
*/
|
2020-10-26 17:25:24 +08:00
|
|
|
protected function handlerExecute($topic, $message)
|
2020-10-10 13:55:14 +08:00
|
|
|
{
|
2021-01-12 17:36:53 +08:00
|
|
|
go(function () use ($topic, $message) {
|
|
|
|
|
try {
|
|
|
|
|
$topic = str_replace('-', '_', $topic);
|
|
|
|
|
|
|
|
|
|
$namespace = 'App\\Kafka\\' . ucfirst($topic) . 'Consumer';
|
|
|
|
|
if (!class_exists($namespace)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2021-02-24 14:11:32 +08:00
|
|
|
$class = Snowflake::createObject($namespace);
|
2021-01-12 17:36:53 +08:00
|
|
|
if (!($class instanceof ConsumerInterface)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2021-02-24 14:11:32 +08:00
|
|
|
$class->onHandler(Snowflake::createObject(Struct::class, [$topic, $message]));
|
2021-01-12 17:36:53 +08:00
|
|
|
} catch (Throwable $exception) {
|
2021-04-01 10:47:28 +08:00
|
|
|
logger()->addError($exception, 'throwable');
|
2020-10-09 17:10:45 +08:00
|
|
|
}
|
2021-01-12 17:36:53 +08:00
|
|
|
});
|
2020-10-09 16:34:33 +08:00
|
|
|
}
|
|
|
|
|
|
2020-10-10 11:30:10 +08:00
|
|
|
|
2020-10-26 16:51:03 +08:00
|
|
|
/**
|
2020-10-28 11:40:33 +08:00
|
|
|
* @param $kafka
|
2020-10-26 16:51:03 +08:00
|
|
|
* @return array
|
2021-03-16 15:46:07 +08:00
|
|
|
* @throws \Exception
|
2020-10-26 16:51:03 +08:00
|
|
|
*/
|
2020-12-17 14:09:14 +08:00
|
|
|
private function kafkaConfig($kafka): array
|
2020-10-26 16:51:03 +08:00
|
|
|
{
|
2020-12-23 16:23:18 +08:00
|
|
|
try {
|
2021-01-14 10:41:16 +08:00
|
|
|
$conf = new Conf();
|
|
|
|
|
$conf->setRebalanceCb([$this, 'rebalanced_cb']);
|
|
|
|
|
$conf->set('group.id', $kafka['groupId']);
|
|
|
|
|
$conf->set('metadata.broker.list', $kafka['brokers']);
|
|
|
|
|
$conf->set('socket.timeout.ms', '30000');
|
|
|
|
|
|
2021-04-01 10:47:28 +08:00
|
|
|
$this->application->debug('kafka listen groupId ' . $kafka['groupId']);
|
|
|
|
|
$this->application->debug('kafka listen brokers ' . $kafka['brokers']);
|
|
|
|
|
|
2021-01-14 10:41:16 +08:00
|
|
|
if (function_exists('pcntl_sigprocmask')) {
|
|
|
|
|
pcntl_sigprocmask(SIG_BLOCK, array(SIGIO));
|
|
|
|
|
$conf->set('internal.termination.signal', (string)SIGIO);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$topicConf = new TopicConf();
|
2020-12-23 16:23:18 +08:00
|
|
|
$topicConf->set('auto.commit.enable', '1');
|
|
|
|
|
$topicConf->set('auto.commit.interval.ms', '100');
|
2021-01-12 17:29:48 +08:00
|
|
|
|
|
|
|
|
//smallest:简单理解为从头开始消费,
|
|
|
|
|
//largest:简单理解为从最新的开始消费
|
2020-12-23 16:23:18 +08:00
|
|
|
$topicConf->set('auto.offset.reset', 'smallest');
|
|
|
|
|
$topicConf->set('offset.store.path', 'kafka_offset.log');
|
2021-01-12 17:23:08 +08:00
|
|
|
$topicConf->set('offset.store.method', 'broker');
|
2021-01-14 10:35:44 +08:00
|
|
|
|
2021-01-14 10:41:16 +08:00
|
|
|
return [$conf, $topicConf, $kafka];
|
2020-12-23 16:23:18 +08:00
|
|
|
} catch (Throwable $exception) {
|
2021-04-01 10:47:28 +08:00
|
|
|
logger()->addError($exception, 'throwable');
|
2021-01-14 10:41:16 +08:00
|
|
|
|
|
|
|
|
return [null, null, null];
|
2020-12-23 16:23:18 +08:00
|
|
|
}
|
2020-10-27 18:46:22 +08:00
|
|
|
|
2020-10-26 16:51:03 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2020-10-26 17:12:16 +08:00
|
|
|
/**
|
|
|
|
|
* @param KafkaConsumer $kafka
|
|
|
|
|
* @param $err
|
|
|
|
|
* @param array|null $partitions
|
2020-10-28 15:54:09 +08:00
|
|
|
* @throws Exception
|
2020-10-26 17:12:16 +08:00
|
|
|
* @throws \Exception
|
|
|
|
|
*/
|
|
|
|
|
public function rebalanced_cb(KafkaConsumer $kafka, $err, array $partitions = null)
|
|
|
|
|
{
|
|
|
|
|
if ($err == RD_KAFKA_RESP_ERR__ASSIGN_PARTITIONS) {
|
|
|
|
|
$kafka->assign($partitions);
|
|
|
|
|
} else if ($err == RD_KAFKA_RESP_ERR__REVOKE_PARTITIONS) {
|
|
|
|
|
$kafka->assign(NULL);
|
|
|
|
|
} else {
|
|
|
|
|
throw new \Exception($err);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2020-10-09 16:34:33 +08:00
|
|
|
}
|