Files
kiri-core/Kafka/Kafka.php
T

193 lines
4.4 KiB
PHP
Raw Normal View History

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\Exception\ConfigException;
use Snowflake\Snowflake;
2020-10-10 13:55:14 +08:00
use Swoole\Coroutine;
2020-10-10 11:30:10 +08:00
use Swoole\Coroutine\Channel;
use Swoole\Coroutine\WaitGroup;
2020-10-09 16:34:33 +08:00
use Swoole\Process;
use Snowflake\Abstracts\Config as SConfig;
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
2020-10-10 11:30:10 +08:00
/**
* @param Process $process
* @throws ConfigException
2020-10-26 17:02:38 +08:00
* @throws \Exception
2020-10-10 11:30:10 +08:00
*/
public function onHandler(Process $process)
{
2020-10-26 16:51:03 +08:00
$this->channelListener();
2020-10-28 11:40:33 +08:00
2020-10-28 11:52:34 +08:00
$waite = new WaitGroup();
2020-10-28 11:53:26 +08:00
$kafkaServers = SConfig::get('kafka.servers');
2020-10-28 11:52:34 +08:00
foreach ($kafkaServers as $kafkaServer) {
$waite->add();
go(function () use ($kafkaServer, $waite) {
defer(function () use ($waite) {
$waite->done();
});
$this->waite($kafkaServer);
});
}
$waite->wait();
2020-10-10 11:30:10 +08:00
}
2020-10-28 11:40:33 +08:00
/**
* @param array $kafkaServer
*/
private function waite(array $kafkaServer)
{
2020-10-28 11:46:43 +08:00
[$config, $topic, $conf] = $this->kafkaConfig($kafkaServer);
2020-10-29 18:17:25 +08:00
$objRdKafka = new Consumer($config);
2020-10-28 11:46:43 +08:00
$topic = $objRdKafka->newTopic($kafkaServer['topic'], $topic);
$topic->consumeStart(0, RD_KAFKA_OFFSET_STORED);
2020-10-28 11:55:11 +08:00
while (true) {
$this->resolve($topic, $conf['interval'] ?? 1000);
}
2020-10-28 11:46:43 +08:00
}
/**
* @param ConsumerTopic $topic
2020-10-28 11:55:30 +08:00
* @param $interval
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) {
$this->channel->push([$message->topic_name, $message]);
} else if ($message->err == RD_KAFKA_RESP_ERR__PARTITION_EOF) {
$this->application->error('No more messages; will wait for more');
} 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) {
2020-10-28 11:46:43 +08:00
$this->application->error($exception->getMessage());
}
2020-10-28 11:40:33 +08:00
}
2020-10-10 11:30:10 +08:00
/**
2020-10-10 11:33:11 +08:00
* 监听通道数据传递
2020-10-10 11:30:10 +08:00
*/
2020-10-26 16:51:03 +08:00
public function channelListener()
2020-10-10 11:30:10 +08:00
{
2020-10-26 16:51:03 +08:00
$this->channel = new Channel(100);
Coroutine::create(function () {
2020-10-10 13:55:14 +08:00
$group = new WaitGroup();
2020-10-26 16:51:03 +08:00
for ($i = 0; $i < 100; $i++) {
2020-10-12 12:51:59 +08:00
$group->add();
go(function () use ($group) {
2020-10-12 12:56:29 +08:00
defer(function () use ($group) {
$group->done();
});
2020-10-12 12:59:29 +08:00
while ($messages = $this->channel->pop()) {
2020-10-26 17:25:24 +08:00
$this->handlerExecute($messages[0], $messages[1]);
2020-10-12 12:51:59 +08:00
}
});
2020-10-10 13:55:14 +08:00
}
$group->wait();
});
}
/**
* @param $topic
* @param $message
*/
2020-10-26 17:25:24 +08:00
protected function handlerExecute($topic, $message)
2020-10-10 13:55:14 +08:00
{
try {
$namespace = 'App\\Kafka\\' . ucfirst($topic) . 'Consumer';
if (!class_exists($namespace)) {
return;
}
$class = Snowflake::createObject($namespace);
if (!($class instanceof ConsumerInterface)) {
return;
2020-10-09 17:10:45 +08:00
}
2020-10-26 17:25:24 +08:00
$class->onHandler(new Struct($topic, $message));
2020-10-29 18:17:25 +08:00
} catch (Throwable $exception) {
2020-10-10 13:55:14 +08:00
$this->application->error($exception->getMessage());
2020-10-10 11:33:11 +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
*/
2020-12-17 14:09:14 +08:00
private function kafkaConfig($kafka): array
2020-10-26 16:51:03 +08:00
{
$conf = new Conf();
2020-10-28 11:57:31 +08:00
$conf->setRebalanceCb([$this, 'rebalanced_cb']);
2020-10-27 19:47:18 +08:00
$conf->set('group.id', $kafka['groupId']);
2020-10-28 11:40:33 +08:00
$conf->set('metadata.broker.list', $kafka['brokers']);
2020-10-30 01:14:05 +08:00
$conf->set('socket.timeout.ms', '30000');
2020-10-28 11:57:31 +08:00
2020-10-27 19:43:25 +08:00
if (function_exists('pcntl_sigprocmask')) {
pcntl_sigprocmask(SIG_BLOCK, array(SIGIO));
2020-10-30 01:14:05 +08:00
$conf->set('internal.termination.signal', (string)SIGIO);
2020-12-02 17:33:31 +08:00
// } else {
// $conf->set('queue.buffering.max.ms', '1');
2020-10-27 19:43:25 +08:00
}
2020-10-28 11:57:31 +08:00
2020-10-27 18:46:22 +08:00
$topicConf = new TopicConf();
2020-10-30 01:14:05 +08:00
$topicConf->set('auto.commit.enable', '1');
$topicConf->set('auto.commit.interval.ms', '100');
2020-10-27 19:43:25 +08:00
//smallest:简单理解为从头开始消费,largest:简单理解为从最新的开始消费
2020-10-27 18:46:22 +08:00
$topicConf->set('auto.offset.reset', 'smallest');
2020-10-27 19:43:25 +08:00
$topicConf->set('offset.store.path', 'kafka_offset.log');
2020-10-27 18:46:22 +08:00
2020-10-27 18:56:32 +08:00
return [$conf, $topicConf, $kafka];
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
}