Files
kiri-core/Kafka/Kafka.php
T

177 lines
3.9 KiB
PHP
Raw Normal View History

2020-10-09 16:34:33 +08:00
<?php
namespace Kafka;
2020-10-26 16:51:03 +08:00
use RdKafka\Conf;
use RdKafka\KafkaConsumer;
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-10 13:55:14 +08:00
use function Amp\stop;
2020-10-09 16:34:33 +08:00
/**
* Class Queue
* @package Queue
*/
class Kafka extends \Snowflake\Process\Process
{
2020-10-26 17:08:52 +08:00
/** @var Channel */
protected $channel;
/**
* @throws ConfigException
*/
// public function initConfig()
// {
// $kafka = SConfig::get('kafka');
// $config = ConsumerConfig::getInstance();
// $config->setMetadataRefreshIntervalMs(
// $kafka['metadataRefreshIntervalMs'] ?? 1000
// );
// $config->setMetadataBrokerList($kafka['brokers']);
// $config->setGroupId($kafka['groupId']);
// $config->setBrokerVersion($kafka['version']);
// $config->setTopics($kafka['topics']);
// $this->channelListener($kafka);
//
// return [new Consumer(), $kafka];
// }
2020-10-10 11:30:10 +08:00
/**
* @param Process $process
* @throws ConfigException
2020-10-26 17:08:52 +08:00
* @throws \RdKafka\Exception
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();
[$config, $conf] = $this->kafkaConfig();
2020-10-26 17:08:52 +08:00
$consumer = new KafkaConsumer($config);
$consumer->subscribe($conf['topics']);
2020-10-26 16:51:03 +08:00
while (true) {
2020-10-26 17:08:52 +08:00
$message = $consumer->consume($conf['metadataRefreshIntervalMs'] ?? 1000);
2020-10-26 16:51:03 +08:00
if (empty($message)) {
continue;
}
switch ($message->err) {
case RD_KAFKA_RESP_ERR_NO_ERROR:
2020-10-26 17:25:24 +08:00
$this->channel->push([$message->topic_name, $message]);
2020-10-26 16:51:03 +08:00
break;
case RD_KAFKA_RESP_ERR__PARTITION_EOF:
2020-10-26 17:02:38 +08:00
$this->application->error('No more messages; will wait for more');
2020-10-26 16:51:03 +08:00
break;
case RD_KAFKA_RESP_ERR__TIMED_OUT:
2020-10-26 17:12:16 +08:00
$this->application->error('Kafka Timed out');
2020-10-26 16:51:03 +08:00
break;
default:
throw new \Exception($message->errstr(), $message->err);
}
2020-10-12 11:51:49 +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 $part
* @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-10 13:55:14 +08:00
} catch (\Throwable $exception) {
$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
/**
* @return array
* @throws ConfigException
*/
private function kafkaConfig()
{
$conf = new Conf();
$kafka = SConfig::get('kafka');
2020-10-26 17:12:16 +08:00
$conf->setRebalanceCb([$this, 'rebalanced_cb']);
2020-10-26 17:50:42 +08:00
$conf->set('group.id', uniqid('kafka'));
2020-10-26 17:12:16 +08:00
2020-10-26 17:50:42 +08:00
$conf->set('metadata.broker.list', 'localhost:9092');
$conf->set('auto.offset.reset', 'earliest');
2020-10-26 16:54:52 +08:00
$conf->set('socket.timeout.ms', 300000);
2020-10-26 16:51:03 +08:00
//多进程和信号
2020-10-26 17:50:42 +08:00
if (function_exists('pcntl_sigprocmask')) {
pcntl_sigprocmask(SIG_BLOCK, array(SIGIO));
$conf->set('internal.termination.signal', SIGIO);
} else {
$conf->set('queue.buffering.max.ms', 1);
}
2020-10-26 16:51:03 +08:00
return [$conf, $kafka];
}
2020-10-26 17:12:16 +08:00
/**
* @param KafkaConsumer $kafka
* @param $err
* @param array|null $partitions
* @throws \RdKafka\Exception
* @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
}