太坑了!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安装环境、测试程序总是会碰到各种各样的问题,就不能顺风顺水,一下就成功吗?转念一想,这不就是我们搬砖人的生活吗?多多积累经验,才会越走越远。

相关推荐
梁萌2 小时前
Linux安装Docker
linux·运维·docker·helloworld·容器化部署
彩虹糖_haha2 小时前
Linux高并发服务器开发 第五天(压缩解压缩/vim编辑器/查找替换/分屏操作/vim的配置)
linux·运维·服务器
旺仔学IT2 小时前
Centos7中使用yum命令时候报错 “Could not resolve host: mirrorlist.centos.org; 未知的错误“
linux·运维·centos
qq_433618443 小时前
shell 编程(五)
linux·运维·服务器
广而不精zhu小白6 小时前
CentOS Stream 9 挂载Windows共享FTP文件夹
linux·windows·centos
一休哥助手6 小时前
全面解析 Linux 系统监控与性能优化
linux·运维·性能优化
二进制杯莫停6 小时前
掌控网络流量的利器:tcconfig
linux
watl07 小时前
【Android】unzip aar删除冲突classes再zip
android·linux·运维
赵大仁7 小时前
在 CentOS 7 上安装 Node.js 20 并升级 GCC、make 和 glibc
linux·运维·服务器·ide·ubuntu·centos·计算机基础
vvw&7 小时前
Docker Build 命令详解:在 Ubuntu 上构建 Docker 镜像教程
linux·运维·服务器·ubuntu·docker·容器·开源