本篇講解如何使用PHP擴展(用了swoole)
簡單使用,自己可以再封裝一下。
使用api時,$conf->set(‘enable.auto.commit’, ‘false’),可以關閉自動提交,進行手動的提交,開啓自動提交。
php swooleProcess.php 使用swoole創建進程開啓消費,判斷topic的分區數創建進程開始消費
topic不存在會自動創建 創建的分區和副本數量默認broker配置,服務器端配置,代碼無法決定!!!
## 是否允許自動創建topic ,若是false,就需要通過命令創建topic
auto.create.topics.enable =true
## 一個topic ,默認分區的replication個數 ,不得大於集羣中broker的個數
default.replication.factor =1
## 每個topic的分區個數,若是在topic創建時候沒有指定的話 會被topic創建時的指定參數覆蓋
num.partitions =1
實例 --replication-factor3--partitions1--topic replicated-topic :名稱replicated-topic有一個分區,分區被複制到三個broker上。
文章最後有conf和topicConf所有配置項。
swooleProcess.php
<?php
/**
* Created by PhpStorm.
* User: zhangwei
* Date: 2019/3/21
* Time: 14:48
*/
include_once ('SwooleProcess.class.php');
include_once ('KafkaHelp.class.php');
$topic = 'test9';
$sp = new SwooleProcess($topic);
<?php
/**
* Created by PhpStorm.
* User: zhangwei
* Date: 2019/3/20
* Time: 8:51
*/
include_once ('./MMysql.class.php');
class KafkaHelp
{
protected $producer;
protected $topic;
/**發送消息到kafka
* @param $playload 消息內容
* @param $key 鍵值
*/
public function produce($playload, $key)
{
$producer = $this->producer();
$topic = $producer->newTopic($this->topic);
//採用默認的隨機方法選擇分區,$key是用來選擇分區的指標
$topic->produce(RD_KAFKA_PARTITION_UA, 0, $playload, $key);
while($producer->getOutQLen() > 0) {
$producer->poll(50);
}
}
/**生產者
* @return \RdKafka\Producer
*/
protected function producer()
{
if(!$this->producer) {
$producer = new \RdKafka\Producer();
$producer->addBrokers($this->getBroker());
$this->producer = $producer;
}
return $this->producer;
}
/**獲取kafka server list
* @return mixed
*/
protected function getBroker()
{
return '127.0.0.1:9092,127.0.0.1:9093,127.0.0.1:9094';
}
/**設置topic
* @param $topic
*/
public function setTopic($topic)
{
$this->topic = $topic;
}
/**
* 獲取分區
* @return mixed
*/
public function getPartitions()
{
$consumer = new \RdKafka\Consumer();
$consumer->addBrokers($this->getBroker());
$topic = $consumer->newTopic($this->topic);
$allInfo = $consumer->getMetadata(false, $topic, 60e3);
$topics = $allInfo->getTopics();
// 循環一個topic
foreach($topics as $tp) {
$partitions = $tp->getPartitions(); // 獲取partion信息
break;
}
// 初次創建 $allIndo get不到Topic的partitions信息
if(!count($partitions)){
return $this->getPartitions();
}
return $partitions;
}
/**
* high level
* @throws Exception
*/
public function highlevelConsumer()
{
$conf = new \RdKafka\Conf();
//這裏就是自動均衡分配分區給消費者的意思
$conf->setRebalanceCb(function(\RdKafka\KafkaConsumer $kafka, $err, array $partitions = null){
switch($err) {
case RD_KAFKA_RESP_ERR__ASSIGN_PARTITIONS:
echo 'assign:\n';
var_dump($partitions);
$kafka->assign($partitions);
break;
case RD_KAFKA_RESP_ERR__REVOKE_PARTITIONS:
$kafka->assign(null);
break;
default:
throw new \Exception($err);
}
});
$conf->set('group.id', 'comsumer-group-'.$this->topic);
$conf->set('client.id', 'client-'.$this->topic);
$conf->set('bootstrap.servers', $this->getBroker());
$conf->set('enable.auto.commit', 'false');
$conf->set('enable.auto.offset.store', 'false');
$topicConf = new \RdKafka\TopicConf();
$topicConf->set('offset.store.method', 'broker');
// 如果沒有檢測到有保存的offset,就從最小開始
$topicConf->set('auto.offset.reset', 'smallest');
$topicConf->set('request.required.acks',-1);
$conf->setDefaultTopicConf($topicConf);
$consumer = new \RdKafka\KafkaConsumer($conf);
$consumer->subscribe([$this->topic]);
while(true) {
$message = $consumer->consume(120 * 1000);
switch($message->err) {
case RD_KAFKA_RESP_ERR_NO_ERROR:
echo $this->topic.'-'.$message->partition.'-'.$message->offset."\n";
// sleep(1);
// 連接數據庫
$mysqlConfig = [
// 'host' => '39.108.219.96',
'host' => '127.0.0.1',
'port' => '3306',
'user' => 'root',
// 'passwd' => '123456',
'passwd' => 'root',
'dbname' => 'kafka'
];
$db = new MMysql($mysqlConfig);
$insertMsg = [
'payload' => $message->payload,
'partition' => $message->partition,
'offset' => $message->offset,
'len' => $message->len,
'timestamp' => $message->timestamp,
'keyvalue' => $message->key,
'topic_name' => $message->topic_name,
];
$resId = $db->insert('consumer',$insertMsg);
// 操作成功提交
if($resId){
$consumer->commitAsync($message);
}else{
var_dump($resId);
}
break;
case RD_KAFKA_RESP_ERR__PARTITION_EOF:
case RD_KAFKA_RESP_ERR__TIMED_OUT:
break;
default:
throw new \Exception($message->errstr(), $message->err);
break;
}
}
}
/**
* low level
* @param $partionId
* @throws Exception
*/
public function createConsumer($partionId)
{
$conf = new \RdKafka\Conf();
//指定消費組
$conf->set('group.id', 'comsumer-group-'.$this->topic);
$conf->set('bootstrap.servers', $this->getBroker());
$rk = new \RdKafka\Consumer($conf);
$topicConf = new \RdKafka\TopicConf();
//自動提交offset間隔時間
$topicConf->set('auto.commit.interval.ms', 100);
//保存offset的方式,可以選擇broker或者file
$topicConf->set('offset.store.method', 'broker');
//如果沒有檢測到有保存的offset,就從最小開始
$topicConf->set('auto.offset.reset', 'smallest');
$topic = $rk->newTopic($this->topic, $topicConf);
$topic->consumeStart($partionId, RD_KAFKA_OFFSET_STORED);
while(true) {
$message = $topic->consume($partionId, 120 * 10000);
switch($message->err) {
case RD_KAFKA_RESP_ERR_NO_ERROR:
echo $this->topic.'-'.$message->partition.'-'.$message->offset."\n";
sleep(1);
break;
case RD_KAFKA_RESP_ERR__PARTITION_EOF:
case RD_KAFKA_RESP_ERR__TIMED_OUT:
break;
default:
throw new \Exception($message->errstr(), $message->err);
break;
}
}
}
}
SwooleProcess.class.php:
<?php
/**
* Created by PhpStorm.
* User: zhangwei
* Date: 2019/3/21
* Time: 14:08
*/
include_once ('KafkaHelp.class.php');
class SwooleProcess{
public $mpid = 0;
public $works = [];
public $kafka;
public $new_index = 0;
public function __construct($topic){
try {
$this->kafka= new KafkaHelp();
$this->kafka->setTopic($topic);
$this->processName = 'kafka-queue:'.$topic;
swoole_set_process_name($this->processName.'master');
$this->mpid = posix_getpid();
$partitions = $this->kafka->getPartitions();
foreach($partitions as $key => $part) {
$partitionId = $part->getId();
$this->createProcess($partitionId);
}
$this->processWait();
}catch (\Exception $e){
die('ALL ERROR: '.$e->getMessage());
}
}
public function run(){
for ($i=0; $i < $this->max_precess; $i++) {
$this->new_index = $i;
$this->CreateProcess();
}
}
public function CreateProcess($partitionId){
$process = new swoole_process(function(swoole_process $worker)use($partitionId){
swoole_set_process_name($this->processName.'-'.$partitionId);
$this->kafka->highlevelConsumer();
}, false, false);
$pid=$process->start();
$this->works[$partitionId]=$pid;
return $pid;
}
public function checkMpid(&$worker){
if(!swoole_process::kill($this->mpid,0)){
// 這句提示,實際是看不到的.需要寫到日誌中
file_put_contents('/tmp/swoole_process_runtime.log', "Master process exited, I [{$worker['pid']}] also quit\n", FILE_APPEND);
$worker->exit();
}
}
/**
* 重啓進程
* @param $ret
* @throws Exception
*/
public function rebootProcess($ret){
$pid=$ret['pid'];
$index=array_search($pid, $this->works);
if($index!==false){
$index=intval($index);
$new_pid=$this->CreateProcess($index);
echo "rebootProcess: {$index}={$new_pid} Done\n";
return;
}
throw new \Exception('rebootProcess Error: no pid');
}
public function processWait(){
while(1) {
if(count($this->works)){
$ret = swoole_process::wait();
if ($ret) {
$this->rebootProcess($ret);
}
}else{
break;
}
}
}
}
MMysql.class.php
<?php
/**
* Desc: php操作mysql的封裝類
* Author zhifeng
* Date: 2017/04/15
* 連接模式:PDO
*/
class MMysql {
protected static $_dbh = null; //靜態屬性,所有數據庫實例共用,避免重複連接數據庫
protected $_dbType = 'mysql';
protected $_pconnect = true; //是否使用長連接
protected $_host = 'localhost';
protected $_port = 3306;
protected $_user = 'root';
protected $_pass = 'root';
protected $_dbName = null; //數據庫名
protected $_sql = false; //最後一條sql語句
protected $_where = '';
protected $_order = '';
protected $_limit = '';
protected $_field = '*';
protected $_clear = 0; //狀態,0表示查詢條件乾淨,1表示查詢條件污染
protected $_trans = 0; //事務指令數
/**
* 初始化類
* @param array $conf 數據庫配置
*/
public function __construct(array $conf) {
class_exists('PDO') or die("PDO: class not exists.");
$this->_host = $conf['host'];
$this->_port = $conf['port'];
$this->_user = $conf['user'];
$this->_pass = $conf['passwd'];
$this->_dbName = $conf['dbname'];
//連接數據庫
if ( is_null(self::$_dbh) ) {
$this->_connect();
}
}
/**
* 連接數據庫的方法
*/
protected function _connect() {
$dsn = $this->_dbType.':host='.$this->_host.';port='.$this->_port.';dbname='.$this->_dbName;
$options = $this->_pconnect ? array(PDO::ATTR_PERSISTENT=>true) : array();
try {
$dbh = new PDO($dsn, $this->_user, $this->_pass, $options);
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); //設置如果sql語句執行錯誤則拋出異常,事務會自動回滾
$dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); //禁用prepared statements的仿真效果(防SQL注入)
} catch (PDOException $e) {
die('Connection failed: ' . $e->getMessage());
}
$dbh->exec('SET NAMES utf8');
self::$_dbh = $dbh;
}
/**
* 字段和表名添加 `符號
* 保證指令中使用關鍵字不出錯 針對mysql
* @param string $value
* @return string
*/
protected function _addChar($value) {
if ('*'==$value || false!==strpos($value,'(') || false!==strpos($value,'.') || false!==strpos($value,'`')) {
//如果包含* 或者 使用了sql方法 則不作處理
} elseif (false === strpos($value,'`') ) {
$value = '`'.trim($value).'`';
}
return $value;
}
/**
* 取得數據表的字段信息
* @param string $tbName 表名
* @return array
*/
protected function _tbFields($tbName) {
$sql = 'SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME="'.$tbName.'" AND TABLE_SCHEMA="'.$this->_dbName.'"';
$stmt = self::$_dbh->prepare($sql);
$stmt->execute();
$result = $stmt->fetchAll(PDO::FETCH_ASSOC);
$ret = array();
foreach ($result as $key=>$value) {
$ret[$value['COLUMN_NAME']] = 1;
}
return $ret;
}
/**
* 過濾並格式化數據表字段
* @param string $tbName 數據表名
* @param array $data POST提交數據
* @return array $newdata
*/
protected function _dataFormat($tbName,$data) {
if (!is_array($data)) return array();
$table_column = $this->_tbFields($tbName);
$ret=array();
foreach ($data as $key=>$val) {
if (!is_scalar($val)) continue; //值不是標量則跳過
if (array_key_exists($key,$table_column)) {
$key = $this->_addChar($key);
if (is_int($val)) {
$val = intval($val);
} elseif (is_float($val)) {
$val = floatval($val);
} elseif (preg_match('/^\(\w*(\+|\-|\*|\/)?\w*\)$/i', $val)) {
// 支持在字段的值裏面直接使用其它字段 ,例如 (score+1) (name) 必須包含括號
$val = $val;
} elseif (is_string($val)) {
$val = '"'.addslashes($val).'"';
}
$ret[$key] = $val;
}
}
return $ret;
}
/**
* 執行查詢 主要針對 SELECT, SHOW 等指令
* @param string $sql sql指令
* @return mixed
*/
protected function _doQuery($sql='') {
$this->_sql = $sql;
$pdostmt = self::$_dbh->prepare($this->_sql); //prepare或者query 返回一個PDOStatement
$pdostmt->execute();
$result = $pdostmt->fetchAll(PDO::FETCH_ASSOC);
return $result;
}
/**
* 執行語句 針對 INSERT, UPDATE 以及DELETE,exec結果返回受影響的行數
* @param string $sql sql指令
* @return integer
*/
protected function _doExec($sql='') {
$this->_sql = $sql;
try {
$res = self::$_dbh->exec($this->_sql);
} catch (PDOException $e) {
die('Exception : ' . $e->getMessage());
}
return $res;
}
/**
* 執行sql語句,自動判斷進行查詢或者執行操作
* @param string $sql SQL指令
* @return mixed
*/
public function doSql($sql='') {
$queryIps = 'INSERT|UPDATE|DELETE|REPLACE|CREATE|DROP|LOAD DATA|SELECT .* INTO|COPY|ALTER|GRANT|REVOKE|LOCK|UNLOCK';
if (preg_match('/^\s*"?(' . $queryIps . ')\s+/i', $sql)) {
return $this->_doExec($sql);
}
else {
//查詢操作
return $this->_doQuery($sql);
}
}
/**
* 獲取最近一次查詢的sql語句
* @return String 執行的SQL
*/
public function getLastSql() {
return $this->_sql;
}
/**
* 插入方法
* @param string $tbName 操作的數據表名
* @param array $data 字段-值的一維數組
* @return int 受影響的行數
*/
public function insert($tbName,array $data){
$data = $this->_dataFormat($tbName,$data);
if (!$data) return;
$sql = "insert into ".$tbName."(".implode(',',array_keys($data)).") values(".implode(',',array_values($data)).")";
return $this->_doExec($sql);
}
/**
* 刪除方法
* @param string $tbName 操作的數據表名
* @return int 受影響的行數
*/
public function delete($tbName) {
//安全考慮,阻止全表刪除
if (!trim($this->_where)) return false;
$sql = "delete from ".$tbName." ".$this->_where;
$this->_clear = 1;
$this->_clear();
return $this->_doExec($sql);
}
/**
* 更新函數
* @param string $tbName 操作的數據表名
* @param array $data 參數數組
* @return int 受影響的行數
*/
public function update($tbName,array $data) {
//安全考慮,阻止全表更新
if (!trim($this->_where)) return false;
$data = $this->_dataFormat($tbName,$data);
if (!$data) return;
$valArr = '';
foreach($data as $k=>$v){
$valArr[] = $k.'='.$v;
}
$valStr = implode(',', $valArr);
$sql = "update ".trim($tbName)." set ".trim($valStr)." ".trim($this->_where);
return $this->_doExec($sql);
}
/**
* 查詢函數
* @param string $tbName 操作的數據表名
* @return array 結果集
*/
public function select($tbName='') {
$sql = "select ".trim($this->_field)." from ".$tbName." ".trim($this->_where)." ".trim($this->_order)." ".trim($this->_limit);
$this->_clear = 1;
$this->_clear();
return $this->_doQuery(trim($sql));
}
/**
* @param mixed $option 組合條件的二維數組,例:$option['field1'] = array(1,'=>','or')
* @return $this
*/
public function where($option) {
if ($this->_clear>0) $this->_clear();
$this->_where = ' where ';
$logic = 'and';
if (is_string($option)) {
$this->_where .= $option;
}
elseif (is_array($option)) {
foreach($option as $k=>$v) {
if (is_array($v)) {
$relative = isset($v[1]) ? $v[1] : '=';
$logic = isset($v[2]) ? $v[2] : 'and';
$condition = ' ('.$this->_addChar($k).' '.$relative.' '.$v[0].') ';
}
else {
$logic = 'and';
$condition = ' ('.$this->_addChar($k).'='.$v.') ';
}
$this->_where .= isset($mark) ? $logic.$condition : $condition;
$mark = 1;
}
}
return $this;
}
/**
* 設置排序
* @param mixed $option 排序條件數組 例:array('sort'=>'desc')
* @return $this
*/
public function order($option) {
if ($this->_clear>0) $this->_clear();
$this->_order = ' order by ';
if (is_string($option)) {
$this->_order .= $option;
}
elseif (is_array($option)) {
foreach($option as $k=>$v){
$order = $this->_addChar($k).' '.$v;
$this->_order .= isset($mark) ? ','.$order : $order;
$mark = 1;
}
}
return $this;
}
/**
* 設置查詢行數及頁數
* @param int $page pageSize不爲空時爲頁數,否則爲行數
* @param int $pageSize 爲空則函數設定取出行數,不爲空則設定取出行數及頁數
* @return $this
*/
public function limit($page,$pageSize=null) {
if ($this->_clear>0) $this->_clear();
if ($pageSize===null) {
$this->_limit = "limit ".$page;
}
else {
$pageval = intval( ($page - 1) * $pageSize);
$this->_limit = "limit ".$pageval.",".$pageSize;
}
return $this;
}
/**
* 設置查詢字段
* @param mixed $field 字段數組
* @return $this
*/
public function field($field){
if ($this->_clear>0) $this->_clear();
if (is_string($field)) {
$field = explode(',', $field);
}
$nField = array_map(array($this,'_addChar'), $field);
$this->_field = implode(',', $nField);
return $this;
}
/**
* 清理標記函數
*/
protected function _clear() {
$this->_where = '';
$this->_order = '';
$this->_limit = '';
$this->_field = '*';
$this->_clear = 0;
}
/**
* 手動清理標記
* @return $this
*/
public function clearKey() {
$this->_clear();
return $this;
}
/**
* 啓動事務
* @return void
*/
public function startTrans() {
//數據rollback 支持
if ($this->_trans==0) self::$_dbh->beginTransaction();
$this->_trans++;
return;
}
/**
* 用於非自動提交狀態下面的查詢提交
* @return boolen
*/
public function commit() {
$result = true;
if ($this->_trans>0) {
$result = self::$_dbh->commit();
$this->_trans = 0;
}
return $result;
}
/**
* 事務回滾
* @return boolen
*/
public function rollback() {
$result = true;
if ($this->_trans>0) {
$result = self::$_dbh->rollback();
$this->_trans = 0;
}
return $result;
}
/**
* 關閉連接
* PHP 在腳本結束時會自動關閉連接。
*/
public function close() {
if (!is_null(self::$_dbh)) self::$_dbh = null;
}
}
conf配置項
array(77) {
["builtin.features"]=>
string(45) "gzip,snappy,sasl,regex,lz4,sasl_plain,plugins"
["client.id"]=>
string(7) "rdkafka"
["message.max.bytes"]=>
string(7) "1000000"
["message.copy.max.bytes"]=>
string(5) "65535"
["receive.message.max.bytes"]=>
string(9) "100000000"
["max.in.flight.requests.per.connection"]=>
string(7) "1000000"
["metadata.request.timeout.ms"]=>
string(5) "60000"
["topic.metadata.refresh.interval.ms"]=>
string(6) "300000"
["metadata.max.age.ms"]=>
string(6) "900000"
["topic.metadata.refresh.fast.interval.ms"]=>
string(3) "250"
["topic.metadata.refresh.fast.cnt"]=>
string(2) "10"
["topic.metadata.refresh.sparse"]=>
string(4) "true"
["debug"]=>
string(0) ""
["socket.timeout.ms"]=>
string(5) "60000"
["socket.blocking.max.ms"]=>
string(4) "1000"
["socket.send.buffer.bytes"]=>
string(1) "0"
["socket.receive.buffer.bytes"]=>
string(1) "0"
["socket.keepalive.enable"]=>
string(5) "false"
["socket.nagle.disable"]=>
string(5) "false"
["socket.max.fails"]=>
string(1) "1"
["broker.address.ttl"]=>
string(4) "1000"
["broker.address.family"]=>
string(3) "any"
["enable.sparse.connections"]=>
string(4) "true"
["reconnect.backoff.jitter.ms"]=>
string(1) "0"
["reconnect.backoff.ms"]=>
string(3) "100"
["reconnect.backoff.max.ms"]=>
string(5) "10000"
["statistics.interval.ms"]=>
string(1) "0"
["enabled_events"]=>
string(1) "0"
["log_cb"]=>
string(14) "0x7f0682d56a60"
["log_level"]=>
string(1) "6"
["log.queue"]=>
string(5) "false"
["log.thread.name"]=>
string(4) "true"
["log.connection.close"]=>
string(4) "true"
["socket_cb"]=>
string(14) "0x7f0682d6d3b0"
["open_cb"]=>
string(14) "0x7f0682d828c0"
["internal.termination.signal"]=>
string(1) "0"
["api.version.request"]=>
string(4) "true"
["api.version.request.timeout.ms"]=>
string(5) "10000"
["api.version.fallback.ms"]=>
string(1) "0"
["broker.version.fallback"]=>
string(6) "0.10.0"
["security.protocol"]=>
string(9) "plaintext"
["sasl.mechanisms"]=>
string(6) "GSSAPI"
["sasl.kerberos.service.name"]=>
string(5) "kafka"
["sasl.kerberos.principal"]=>
string(11) "kafkaclient"
["sasl.kerberos.kinit.cmd"]=>
string(114) "kinit -S "%{sasl.kerberos.service.name}/%{broker.name}" -k -t "%{sasl.kerberos.keytab}" %{sasl.kerberos.principal}"
["sasl.kerberos.min.time.before.relogin"]=>
string(5) "60000"
["group.id"]=>
string(5) "test8"
["partition.assignment.strategy"]=>
string(16) "range,roundrobin"
["session.timeout.ms"]=>
string(5) "10000"
["heartbeat.interval.ms"]=>
string(4) "3000"
["group.protocol.type"]=>
string(8) "consumer"
["coordinator.query.interval.ms"]=>
string(6) "600000"
["max.poll.interval.ms"]=>
string(6) "300000"
["enable.auto.commit"]=>
string(4) "true"
["auto.commit.interval.ms"]=>
string(4) "5000"
["enable.auto.offset.store"]=>
string(4) "true"
["queued.min.messages"]=>
string(6) "100000"
["queued.max.messages.kbytes"]=>
string(7) "1048576"
["fetch.wait.max.ms"]=>
string(3) "100"
["fetch.message.max.bytes"]=>
string(7) "1048576"
["fetch.max.bytes"]=>
string(8) "52428800"
["fetch.min.bytes"]=>
string(1) "1"
["fetch.error.backoff.ms"]=>
string(3) "500"
["offset.store.method"]=>
string(6) "broker"
["enable.partition.eof"]=>
string(5) "false"
["check.crcs"]=>
string(5) "false"
["enable.idempotence"]=>
string(5) "false"
["enable.gapless.guarantee"]=>
string(5) "false"
["queue.buffering.max.messages"]=>
string(6) "100000"
["queue.buffering.max.kbytes"]=>
string(7) "1048576"
["queue.buffering.max.ms"]=>
string(1) "0"
["message.send.max.retries"]=>
string(1) "2"
["retry.backoff.ms"]=>
string(3) "100"
["queue.buffering.backpressure.threshold"]=>
string(1) "1"
["compression.codec"]=>
string(4) "none"
["batch.num.messages"]=>
string(5) "10000"
["delivery.report.only.error"]=>
string(5) "false"
}
topicConf配置項
array(15) {
["request.required.acks"]=>
string(2) "-1"
["request.timeout.ms"]=>
string(4) "5000"
["message.timeout.ms"]=>
string(6) "300000"
["queuing.strategy"]=>
string(4) "fifo"
["produce.offset.report"]=>
string(5) "false"
["partitioner"]=>
string(17) "consistent_random"
["compression.codec"]=>
string(7) "inherit"
["compression.level"]=>
string(2) "-1"
["auto.commit.enable"]=>
string(4) "true"
["auto.commit.interval.ms"]=>
string(5) "60000"
["auto.offset.reset"]=>
string(7) "largest"
["offset.store.path"]=>
string(4) "/tmp"
["offset.store.sync.interval.ms"]=>
string(2) "-1"
["offset.store.method"]=>
string(6) "broker"
["consume.callback.max.messages"]=>
string(1) "0"
}