太坑了!RabbitMQ+PHP开发的辛酸经历

公司需要与另一家公司对接数据,他们指定用RabbitMQ,之前我们也用过MQTT,KAFKA之类的,这个我是第一次接触,想着应该跟kafka差不多,结果过程很不顺利,耽误了一些时间,记录下我的心酸历程。

docker安装RabbitMQ

我现在安装一些环境,基本能用docker就用docker,太方便了。安装比较简单,两个命令搞定:

bash 复制代码
docker pull rabbitmq:3-management
docker run -d --hostname my-rabbit --name rabbit -v /data/rabbitmq:/var/lib/rabbitmq -e RABBITMQ_DEFAULT_USER=admin -e RABBITMQ_DEFAULT_PASS=admin -p 15672:15672 -p 5672:5672

注释:第二个命令中,--name后面的容器名称可以自定义,-v后面的挂载目录的映射关系也可以根据自己实际情况自定义,同样后面的RABBITMQ_DEFAULT_USER``和RABBITMQ_DEFAULT_PASS,web登录账号密码也可以自定义。运行起来后就可以在浏览器输入 服务器ip:15672访问了,输入上面设置的账号密码即可,非常简单。

安装PHP的amqp扩展

我们想要在PHP中使用RabbitMQ,跟kafka一样,必须要安装相应的扩展,否则程序是运行不起来的。这一步选扩展版本很重要,最新版本是v2.0.0,我就喜欢用最新的版本,但此刻还是建议你用v1.11.0(下面说原因)。此处坑一来了,如果你先直接安装这个扩展,肯定会报以下错误: checking for amqp using pkg-config... configure: error: librabbitmq not found 所以我们要先安装下librabbitmq

bash 复制代码
wget https://github.com/alanxz/rabbitmq-c/archive/v0.9.0.tar.gz
tar -xvf v0.9.0.tar.gz
cd rabbitmq-c-0.9.0
cmake . -DCMAKE_INSTALL_PREFIX=/home/rabbitmq-c-0.9.0 #指定安装目录
make
make install

上述步骤中,如果没有cmake命令,可以安装下yum -y install cmake;安装成功后我们就可以继续安装我们的amqp扩展了。

bash 复制代码
wget https://pecl.php.net/get/amqp-2.0.0.tgz #下载
tar -xvf amqp-2.0.0.tgz #解压
cd amqp-2.0.0
/home/rl/php7.4.33/bin/phpize #用phpize生成编译文件,注意查看你的php在哪里
./configure --with-php-config=/home/rl/php7.4.33/bin/php-config --with-amqp --with-librabbitmq-dir=/home/rabbitmq-c-0.9.0
make && make install

经过上面的操作,我们已经编译生成了amqp.so文件在PHP扩展里了,剩下的就是在php.ini配置中加入extension=amqp.so,重启下php服务就行了,用php -m命令查看下,amqp扩展就有了。下面写个使用例子。

使用PHP写个生产消费的例子

  • 生产者代码:
php 复制代码
<?php
date_default_timezone_set("Asia/Shanghai");
//配置信息 安装RabbitMQ设置的账号密码等
$amqp_config = array( 
    'host' => '你的ip',  
    'port' => '5672',  
    'login' => 'admin',  
    'password' => 'admin', 
    'vhost'=>'/' 
);   
$e_name = 'e_linvo'; //交换机名 
//$q_name = 'q_linvo'; //无需队列名 
$k_route = 'key_1'; //路由key 
 
//创建连接和channel 
$amqp = new AMQPConnection($amqp_config);   
if (!$amqp->connect()) {   
    die("Cannot connect to the broker!\n");   
}   
$channel = new AMQPChannel($amqp);   

//创建交换机对象    
$ex = new AMQPExchange($channel);   
$ex->setName($e_name);   

//发送消息 
//$channel->startTransaction(); //开始事务  
for($i=0; $i<10; ++$i){ 
    //消息内容 
    $message = "$i hello RabbitMQ !";   
    echo "Send Message:".$ex->publish($message, $k_route)."\n";  
} 
//$channel->commitTransaction(); //提交事务 
$amqp->disconnect();

这里就要注意了,坑二来了,执行上面代码,一直报错Cannot connect to the broker!,就是连接的时候代码里抛出的错误,我以为自己设置的账号密码不对,或者安装的RabbitMQ不对,又或者是5672端口没开,调试半天,都还是一直报这个错误,打印连接返回值,发现是null。反正我就觉得不知道什么原因导致的连不上RabbitMQ。后来我想,连接返回值要么是true,要么是false,怎么会是null,只有没有返回值的函数打印才是null,于是我把判断连接的代码注释掉,发现消息正常生产了。 为了验证我的猜想,我去github上看了下扩展的C源码,找到源码里RabbitMQ连接部分,即amqp_connection.c文件。如下图

从上面源码看,连接函数里如果不是持久连接(if里面部分)确实没有返回值,除了该函数上面的有返回值,其他几个函数都是没有写返回值的,所以我们代码里就不能去判断是否连接成功。 既然测试代码里有判断,那肯定之前是有返回值的,我切换了几个版本看了下,发现v2.0系列的都把返回值去掉了,v1.11.0以及之前的版本都是有返回值的,所以上面我会建议还是安装V1.11.0版本的扩展,当然如果不写判断是否连接成功,安装最新版也是没问题的。下面提供下消费端测试代码,感兴趣的同学可以研究下:

php 复制代码
<?php
//配置信息 
$conn_args = array( 
    'host' => '你的ip',  
    'port' => '5672',  
    'login' => 'admin',  
    'password' => 'admin', 
    'vhost'=>'/' 
);   
$e_name = 'e_linvo'; //交换机名 
$q_name = 'q_linvo'; //队列名 
$k_route = 'key_1'; //路由key 
 
//创建连接和channel 
$conn = new AMQPConnection($conn_args);
if (!$conn->connect()) {   
    die("Cannot connect to the broker!\n"); 
}   
$channel = new AMQPChannel($conn);   
 
//创建交换机    
$ex = new AMQPExchange($channel);   
$ex->setName($e_name); 
$ex->setType(AMQP_EX_TYPE_DIRECT); //direct类型  
$ex->setFlags(AMQP_DURABLE); //持久化 
echo "Exchange Status:".$ex->declare()."\n";   
   
//创建队列    
$q = new AMQPQueue($channel); 
$q->setName($q_name);   
$q->setFlags(AMQP_DURABLE); //持久化  
echo "Message Total:".$q->declare()."\n";   
 
//绑定交换机与队列,并指定路由键 
echo 'Queue Bind: '.$q->bind($e_name, $k_route)."\n"; 
 
//阻塞模式接收消息 
echo "Message:\n";   
while(True){ 
    $q->consume('processMessage');   
    //$q->consume('processMessage', AMQP_AUTOACK); //自动ACK应答  
} 
$conn->disconnect();   
 
/**
 * 消费回调函数
 * 处理消息
 */ 
function processMessage($envelope, $queue) { 
    $msg = $envelope->getBody(); 
    echo $msg."\n"; //处理消息 
    $queue->ack($envelope->getDeliveryTag()); //手动发送ACK应答 
}

结语

每次碰到这样的问题,我总是在想,为什么Linux安装环境、测试程序总是会碰到各种各样的问题,就不能顺风顺水,一下就成功吗?转念一想,这不就是我们搬砖人的生活吗?多多积累经验,才会越走越远。

相关推荐
weixin_4370446428 分钟前
Netbox批量添加设备——堆叠设备
linux·网络·python
hhy_smile28 分钟前
Ubuntu24.04 环境配置自动脚本
linux·ubuntu·自动化·bash
宴之敖者、1 小时前
Linux——\r,\n和缓冲区
linux·运维·服务器
LuDvei1 小时前
LINUX错误提示函数
linux·运维·服务器
未来可期LJ1 小时前
【Linux 系统】进程间的通信方式
linux·服务器
Abona1 小时前
C语言嵌入式全栈Demo
linux·c语言·面试
Lenyiin1 小时前
Linux 基础IO
java·linux·服务器
The Chosen One9851 小时前
【Linux】深入理解Linux进程(一):PCB结构、Fork创建与状态切换详解
linux·运维·服务器
Kira Skyler2 小时前
eBPF debugfs中的追踪点format实现原理
linux
2501_927773073 小时前
uboot挂载
linux·运维·服务器