简单测试

环境:Centos6.4,PHP7,kafka服务器IP:192.168.9.154,PHP服务器:192.168.9.157

在192.168.9.157创建目录和文件。

//生产者
<?php
require './modules/kafka.php';
$rk = new kafka();
$rk->send(['hello my kafka']);
echo 'OK~';
//消费者
<?php
require './modules/kafka.php';
$rk = new kafka();
$rk->consumer();
//Kafka类
<?php
class kafka
{
public $broker_list = '192.168.9.154:9092'; //现在是一个,也可以多个,用逗号隔开
public $topic = 'mytopic'; //定义topic
public $partition = ; //定义topic的物理分组,这里是0
public $logFile = './kafkalog/info.log'; //计划在消费的时候写日志,指定日志文件 protected $producer = null; //存储producer对象
protected $consumer = null; //存储consumer对象 public function __construct()
{ if (empty($this->broker_list))
{
echo 'broker not config';
}
$rk = new \Rdkafka\Producer(); //实例化对象 if (empty($rk)) {
echo 'producer error1';
}
$rk->setLogLevel(LOG_DEBUG); //设置错误级别
if(!$rk->addBrokers($this->broker_list)) {//设置Broker地址
echo 'producer error2';
}
$this->producer = $rk;
} //生产者的方法(生产者把日志向消息队列发送)
public function send($message = [])
{ $topic = $this->producer->newTopic($this->topic); //创建topic $topic->produce(RD_KAFKA_PARTITION_UA, $this->partition, json_encode([$message]); //生产
}
//消费者方法 (监听消息队列)
public function consumer()
{
$conf = new \Rdkafka\Conf();
$conf->set('group.id', );
$conf->set('metadata.broker.list', $this->broker_list);
$topicConf = new \Rdkafka\topicConf();
$topicConf->set('auto.offset.reset', 'smallest'); $conf->setDefaultTopicConf($topicConf); $consumer = new \Rdkafka\kafkaConsumer($conf); $consumer->subscribe([$this->topic]); //订阅 echo "wating for message....\n"; while(true) {
$message = $consumer->consume(*);
switch ($message->err) {
case RD_KAFKA_RESP_ERR_NO_ERROR:
echo '要处理消息了~~~';
$messageInfo = $message->payload;
// echo $messageInfo."\n";
break;
}
sleep();
}
}
}

记住消费者PHP文件要在终端运行:php consumer.php。

这里就不测试了。

工作代码

/**
* 将用户的登陆信息放到 Kafka
*
*/
public function sendCustomerLoginInfoToKafka($param){
$customerLoginInfoServiceClient = new CustomerLoginInfoServiceClient();
$msg = json_encode($param);
$topic=isset(Yii::app()->params['customer_login_info_topic'])?Yii::app()->params['customer_login_info_topic']:'e_user_login_info';
$result = $customerLoginInfoServiceClient->add($topic, $msg);
}
/**
* 客户登陆信息 服务化接口调用client端
*/
class CustomerLoginInfoServiceClient { public function add($topic, $msg) {
//直接进kafka不再调用java服务
EdjKafka::getInstance()->putin(array("topic" => $topic, "payload" => $msg));
} }
class EdjKafka {

    private static $instance;

    public static function getInstance($className=__CLASS__) {
if (empty(self::$instance)) {
self::$instance = new $className();
}
return self::$instance;
} public function putin($params) {
$task = array(
'class' => __CLASS__,
'method' => 'putin_job',
'params' => $params,
);
Queue::model()->putin($task, 'phptokafka_0000');
} public function putin_job($params) {
KafkaProducer::getInstance()->putin($params["topic"], $params["payload"]);
}
}
<?php
require_once(Yii::app()->basePath.'/vendors/kafka/autoload.php'); //kafka包在最下面 class KafkaProducer { private static $instance;
private $producer;
private $partitionCountMap = array(); public static function getInstance($className=__CLASS__) {
if (empty(self::$instance)) {
self::$instance = new $className();
}
return self::$instance;
} public function __construct() {
$brokers = Yii::app()->params['kafka']['brokers']; $newProducer = \Kafka\Produce::getInstance($brokers, , $brokers);
$newProducer->setRequireAck(-);
$this->producer = $newProducer;
} private function getPartitionCount($topic, $force=false) {
$now = time(); //3分钟查询一次patition
if( !$force && array_key_exists($topic, $this->partitionCountMap) && $this->partitionCountMap[$topic]["expire"] > $now ) {
return $this->partitionCountMap[$topic]["count"];
} //获取到topic下可用的partitions
$this->producer->getClient()->refreshMetadata();
$partitions = $this->producer->getAvailablePartitions($topic);
EdjLog::info(__METHOD__.'|'.$topic.'|get partition|'.json_encode($partitions));
$partitionCount = count($partitions);
if ($partitionCount == ) {
EdjLog::error(__METHOD__."|".$topic."|topic partitions count 0");
} $this->partitionCountMap[$topic] = array("count" => $partitionCount, "expire" => $now + ); return $partitionCount;
} public function putin($topic, $payload) {
if(empty($topic)) {
return;
} $partitionCount = $this->getPartitionCount($topic); if ($partitionCount != ) {
try {
$pid = time() % $partitionCount;
$this->producer->setMessages($topic, $pid, array($payload));
$result = $this->producer->send();
EdjLog::debugLog(__METHOD__.'|'.$topic.'|'.$pid);
} catch (\Kafka\Exception $e) {
EdjLog::error(__METHOD__.'|'.$e->getMessage());
$this->getPartitionCount($topic, true);
}
}
}
}
<?php
return array(
'brokers' => "123.123.123.123:9092,123.123.123.123:9093,123.123.123.123:9094", //ip一样,端口不一样
//topic名的映射,推荐用class名字做key
//测试环境和线上用不同的配置文件
'topicmap' => array(
"RDriverPositionToKafka" => "driver_location_test",
"ROrderToKafka" => "order_test",
"SubmitOrderAutoService_saveOrderInfoJob" => "finished_order_picture",
'vip_customer_change' => 'vip_customer_change',
),
);
链接:https://pan.baidu.com/s/1xiHAt8mbxpdPLhqZbKL1LQ
提取码:l92h //kafka包
<?php
/**
* 基于redis的queue队列
*/
class Queue {
private static $_models; public $queue_max_length = array(
); public static function model($className=__CLASS__) {
$model=null;
if (isset(self::$_models[$className]))
$model=self::$_models[$className];
else {
$model=self::$_models[$className]=new $className(null);
}
return $model;
} //确定redis
private function select_redis($type) {
return QueuePool::model()->get_zone($type);
} private function trim($queue_name) { $type = str_replace("queue_", "", $queue_name);
$max = ;
if (isset($this->queue_max_length[$type])) {
$max = intval($this->queue_max_length[$type]);
}
if ($max>) {
$zone = $this->select_redis($type);
if($zone) {
$zone['redis']->lTrim($queue_name, , $max-);
}
else {
EdjLog::error("can not find zone, queue name: " . $type);
return;
}
}
} /**
* 放入队列,统一队列对外暴露方法,增加类型默认放task队列,指定了就放对应的队列,同时如果不在指定类型内的,也放默认队列
*
* @author sunhongjing 2013-07-04
* @param unknown_type $params
* @param unknown_type $type
* @return mixed
*/
public function putin($params=null, $type){
$type = empty($type) ? 'error' : strtolower($type); $base_qname = QNameManagerService::model()->get_base_qname($type); if(!empty($base_qname)) {
$this->queue_name = 'queue_'.$base_qname;
}else{
$this->queue_name = 'queue_error';
} if ($params===null) {
return $this->get();
} else {
return $this->add($params);
}
} /**
* 取一条队列数据,封装多个队列,统一调用方法
* @author sunhongjing 2013-07-09
* @param string $type
* @return array
*/
public function getit($type='default')
{
$base_qname = QNameManagerService::model()->get_base_qname($type); if(!empty($base_qname)) {
$this->queue_name = 'queue_'.$base_qname;
}else{
return array();
} $zone = $this->select_redis($type);
if($zone) {
if($zone['brpop']) {
$json = '';
$result = $zone['redis']->brPop($this->queue_name, $zone['brpop']);
if(!empty($result) && isset($result[])) {
$json = $result[];
}
}
else {
$json = $zone['redis']->rPop($this->queue_name);
}
}
else {
EdjLog::error("can not find zone, queue name: " . $type);
return array();
} return json_decode($json, true);
} /**
* 返回队列接收的类型列表
* @author sunhongjing 2013-07-04
* @return array
*/
public function getQueueTypeList()
{
$list = QNameManager::model()->findall();
if($list) {
return $list;
} EdjLog::error("Error: get queue list from database");
return array();
} /**
* 设置或者读取位置队列
* @param array $params
* @return mixed
*/
public function position($params=null) {
$this->queue_name='queue_position'; if ($params===null) {
return $this->get();
} else {
return $this->add($params);
}
} /**
* 心跳队列
* @param string $params
* @return mixed
*/
public function heartbeat($params=null) {
$this->queue_name='queue_heartbeat'; if ($params===null) {
return $this->get();
} else {
return $this->add($params);
}
} /**
* 最高优先级队列
* @param string $params
* @return mixed
*/
public function task($params=null) {
$this->queue_name='queue_task'; if ($params===null) {
return $this->get();
} else {
return $this->add($params);
}
} /**
* 保存日志到数据库
* @param string $params
* @return mixed
*/
public function dumplog($params=null) {
$this->queue_name='queue_dumplog'; if ($params===null) {
return $this->get();
} else {
return $this->add($params);
}
} /**
* 返回各个队列中的任务总数
*/
public function length() { $queue = $this->getQueueTypeList(); $queue_length=array();
$reg = "/P[0-9]+$/";
foreach($queue as $item) {
$base_qname = $item->base_qname;
$zone = $this->select_redis($base_qname);
$key = 'queue_'.$base_qname;
if($zone) {
$len = $zone['redis']->lLen($key);
if(isset($item->max) && $len > $item->max) {
$key = '!'.$key;
} $pkey = '';
if(preg_match($reg, $zone['name'])) {
$pkey = $key.'@'.$zone['name'];
}
else {
$pkey = $key.'@'.$zone['name']."_P".$item->level;
} $queue_length[$pkey] = $len;
}
else {
EdjLog::error("can not find zone, queue name: " . $key);
}
} return $queue_length;
} private function get() {
$type = str_replace("queue_", "", $this->queue_name);
$zone = $this->select_redis($type);
if($zone) {
if($zone['brpop']) {
$json = '';
$result = $zone['redis']->brPop($this->queue_name, $zone['brpop']);
if(!empty($result) && isset($result[])) {
$json = $result[];
}
}
else {
$json = $zone['redis']->rPop($this->queue_name);
}
}
else {
EdjLog::error("can not find zone, queue name: " . $type);
return array();
}
return json_decode($json, true);
} private function add($params) {
$json=json_encode($params);
$type = str_replace("queue_", "", $this->queue_name);
$zone = $this->select_redis($type);
$return = ;
if($zone) {
try {
$return = $zone['redis']->lPush($this->queue_name, $json);
} catch (Exception $e) {
EdjLog::error("write redis error,msg:".$e->getMessage());
//echo $e->getMessage();
}
}
else {
EdjLog::error("can not find zone, queue name: " . $type);
} return $return;
} public function processTask($task) {
if(!isset($task['method'], $task['params'])) {
$task_content = json_encode($task);
EdjLog::error("can not run task due to no 'method' or 'params' specified, task is $task_content");
return;
} $method=$task['method'];
$params=$task['params'];
$class = isset($task['class']) ? $task['class'] : "QueueProcess";
EdjLog::info("REDIS_QUEUE_OUT CLASS:$class METHOD:$method PARAMS:".json_encode($params));
try {
//throw new Exception("Value must be 1 or below");
$queue_process=new $class();
// check this method is exist, if not throw ReflectionException
new ReflectionMethod($queue_process, $method);
call_user_func_array(array($queue_process, $method), array($params));
} catch(Exception $e) {
$errmsg = $e->getMessage();
EdjLog::error("execption queue_run method:$method err: $errmsg");
}
} public function getLengthByType($type){
$type = empty($type) ? 'error' : strtolower($type);
$base_qname = QNameManagerService::model()->get_base_qname($type);
$zone = $this->select_redis($base_qname);
$key = 'queue_'.$base_qname;
$len = ;
if($zone) {
$len = $zone['redis']->lLen($key);
} else {
EdjLog::error("can not find zone, queue name: " . $base_qname);
}
return $len;
}
}

Kafka学习之(四)PHP操作Kafka的更多相关文章

  1. Git 学习(四)操作修改和版本穿梭

    Git 学习(四)操作修改和版本穿梭 之前的章节,已介绍了本地Git库创建.暂存区增.删.改,以及提交版本库:可回顾下命令操作: git add 和 git commit. 光有之前章节的操作,Git ...

  2. Docker学习(四): 操作容器

    特别声明: 博文主要是学习过程中的知识整理,以便之后的查阅回顾.部分内容来源于网络(如有摘录未标注请指出).内容如有差错,也欢迎指正! =============系列文章============= 1 ...

  3. GO学习-(30) Go语言操作kafka

    go操作kafka Kafka是一种高吞吐量的分布式发布订阅消息系统,它可以处理消费者规模的网站中的所有动作流数据,具有高性能.持久化.多副本备份.横向扩展等特点.本文介绍了如何使用Go语言发送和接收 ...

  4. ELK+Kafka学习笔记之搭建ELK+Kafka日志收集系统集群

    0x00 概述 关于如何搭建ELK部分,请参考这篇文章,https://www.cnblogs.com/JetpropelledSnake/p/9893566.html. 该篇用户为非root,使用用 ...

  5. Kafka学习笔记(6)----Kafka使用Producer发送消息

    1. Kafka的Producer 不论将kafka作为什么样的用途,都少不了的向Broker发送数据或接受数据,Producer就是用于向Kafka发送数据.如下: 2. 添加依赖 pom.xml文 ...

  6. kafka学习笔记(三)kafka的使用技巧

    概述 上一篇随笔主要介绍了kafka的基本使用包括集群参数,生产者基本使用,consumer基本使用,现在来介绍一下kafka的使用技巧. 分区机制 我们在使用 Apache Kafka 生产和消费消 ...

  7. Kafka学习笔记(1)----Kafka的简介和Linux下单机安装

    1. Kafka简介 Kafka is a distributed,partitioned,replicated commit logservice.它提供了类似于JMS的特性,但是在设计实现上完全不 ...

  8. 【kafka学习笔记】PHP接入kafka

    安装扩展 # 先安装rdkfka库文件 git clone https://github.com/edenhill/librdkafka.git 或者: wget https://gitee.com/ ...

  9. kafka学习(四)kafka安装与命令行调用

    文章更新时间:2020/06/07 一.安装JDK 过程就不过多介绍了... 二.安装Zookeeper 安装过程可以参考此处~ 三.安装并配置kafka Kafka下载地址  http://kafk ...

  10. Kafka学习笔记-Java简单操作

    Maven依赖包: <dependency> <groupId>org.apache.kafka</groupId> <artifactId>kafka ...

随机推荐

  1. Zabbix-3.0.x使用OneAlert发送告警

    导读 OneAlert 是国内首个 SaaS 模式的云告警平台,集成国内外主流监控/支撑系统,实现一个平台上集中处理所有 IT 事件,提升 IT 可靠性.它能以史上第二快的速度,对事件进行智能的组织. ...

  2. phpcms 列表页中调用其下的所有子栏目(或特定的子栏目)的方法

    phpcms 列表页中,如何调用其下的所有子栏目(或特定的子栏目),具体的写法如下,感兴趣的朋友可以参考下,希望对大家有所帮助 代码如下: {pc:get sql="select * fro ...

  3. 【BZOJ2560】串珠子 状压DP+容斥

    [BZOJ2560]串珠子 Description 铭铭有n个十分漂亮的珠子和若干根颜色不同的绳子.现在铭铭想用绳子把所有的珠子连接成一个整体. 现在已知所有珠子互不相同,用整数1到n编号.对于第i个 ...

  4. nginx提高加载静态文件速度

    1.本来对于静态网页,我们不需要放在应用容器中,原因一时由于应用服务器是用来解析动态网页的,针对静态网页本来就性能不高,而且还会占用应用容器的资源,所以我们专门使用nginx用来解析静态网页.     ...

  5. 170321、Spring+Quartz实现定时任务的配置方法

    Quartz是Java版开源定时调度器. 核心概念: Job 表示一个工作,要执行的具体内容.此接口中只有一个方法 void execute(JobExecutionContext context): ...

  6. JUnit常用断言及注解

    断言是编写测试用例的核心实现方式,即期望值是多少,测试的结果是多少,以此来判断测试是否通过. 断言核心方法   assertArrayEquals(expecteds, actuals) 查看两个数组 ...

  7. PowerDesigner概念模型与物理模型相互转换及导出数据字典

    最近公司项目竣工,验收完成后,把整体平台的所有文档都写清楚,找包发给甲方,由于本人是维护数据库工作,依上面要求,必须编写<数据库设计说明书>里面格式包含三个部分:概念模型.物理模型.数据字 ...

  8. Codeforces Round #427 (Div. 2)—A,B,C,D题

    A. Key races 题目链接:http://codeforces.com/contest/835/problem/A 题目意思:两个比赛打字,每个人有两个参数v和t,v秒表示他打每个字需要多久时 ...

  9. CLR via 笔记4.2 类型转换 is 与 as 区别

    is 和 as 操作符是用来进行强制类型转换的 is : 检查一个对象是否兼容于其他指定的类型,并返回一个Bool值,永远不会抛出异常 object o = new object(); if (o i ...

  10. Limits on Table Column Count and Row Size Databases and Tables Table Size 最大行数

    MySQL :: MySQL 8.0 Reference Manual :: C.10.4 Limits on Table Column Count and Row Size https://dev. ...