PHP RabbitMQ连接超时问题

问题背景

Error: The connection timed out after 3 sec while awaiting incoming data

看到这个报错,我不以为意,认为是我设置的超时时间不够导致的,那就设置长一点

Error: The connection timed out after 300 sec while awaiting incoming data

当我等待了300秒看到了这个报错,我眉头一皱,此事比不简单。

错误分析

很明显这个问题有几种可能,我一一排查

因为我是从测试环境转到生产环境的,代码一样,测试环境没有问题,只是RabbitMQ不一样。

首先测试MQ是否能通

使用telnet 命令测试,发现不能通,原来是生产环境的MQ加了IP限制。把IP加到白名单后能通,但是代码连接仍然超时。

测试我怀疑是不是给过来的端口不对,因为测试环境的端口是5672,这个是默认端口,而生产环境的端口是5671,修改端口后发现端口不通,连接失败,拒绝连接。那就端口没有问题

接着我认为可能是账号密码不对,修改后发现报错还是超时,说明代码都还没连上RabbitMQ。
连接代码

此时我去网上寻找答案,恰巧也有人跟我遇到了类似的问题,在github上向开发者提了Issuses

PHP 致命错误:未捕获的异常"PhpAmqpLib\Exception\AMQPTimeoutException",并显示消息"在等待传入数据时,连接在 3 秒后超时" ·问题 #839 ·php-amqplib/php-amqplib ·GitHub的

其中这个提问跟我遇到的问题完全一致

扩展的开发者也对这个问题进行了答复,从这里我就知道这个问题跟SSL有关系了,同时还提供了样例php-amqplib/demo/ssl_connection.php at master · php-amqplib/php-amqplib · GitHub

根据这个样例不难看出是要我们使用ssl进行连接,因为MQ的5671端口是支持SSL的。

可是很多人都跟我一样,没有这个证书,或者说即使有也不知道怎么使用。确实,我也是一脸懵,就像开发者说的,你需要深入学习一下TLS/SSL的工作原理

当然了,这都不是我们的重点,重点是解决当前遇到的问题。

有了样例,那我们直接拿过来用

php 复制代码
$sslOptions = array(
    'cafile' => CERTS_PATH . '/ca_certificate.pem',
    'local_cert' => CERTS_PATH . '/client_certificate.pem',
    'local_pk' => CERTS_PATH . '/client_key.pem',
    'verify_peer' => true,
    'verify_peer_name' => false,
);

$connection = new AMQPSSLConnection(HOST, 5671, USER, PASS, VHOST, $sslOptions);

此时会发现能连上了,但是运行会提示一个信息

不是吧!我刚找的解决方案你就打算弃用了,也不难理解。为什么同样是连接却要用两种方法实现,随着我查看源码我就发现了。

先看最开始连接的方法:AMQPStreamConnection

一般很少人会去看这个,因为这个方法只有前面4个参数是必填的,后面都有默认值,例如最开始报错超时3秒也是在这设置。这里我们就看到在参数里篇有个ssl_protocol和?AMQPConnectionConfig config两个参数,不难看出这个就是能够支持SSL的,这也是AMQPSSLConnection方法会弃用的原因,不然我本地用着AMQPStreamConnection方法好好的,到其他地方,用不了了。

知道了AMQPStreamConnection可以支持SSL了,我们看一下AMQPConnectionConfig类

这个类是用来设置连接参数的

根据样例我们不难设置这个ssl相关的数据,不过因为我没有证书,所以自然不用去管这个。

接下来回头看看支持SSL的类AMQPSSLConnection

对的,你没看错,AMQPSSLConnection继承了AMQPStreamConnection,合着搞了大半天你们本来就是一起的。这里我们看到$ssl_options参数就是样例中ssl参数配置的位置

弄完之后AMQPSSLConnection又构建父类初始化,就是还是AMQPSSLConnection,把ssl参数加上,加的参数位置是$context,直接把方法拿过来用就可以了

AMQPSSLConnection并没有用AMQPConnectionConfig来设置ssl参数,那我们也可以不用,直接整

解决方案

php 复制代码
        $sslOptions = array(
            'verify_peer' => false,
        );
        $ssl_context = stream_context_create();
        foreach ($sslOptions as $k => $v) {
            stream_context_set_option($ssl_context, 'ssl', $k, $v);
        }
        
        $connection = new \PhpAmqpLib\Connection\AMQPStreamConnection(
            $config['host'],
            $config['port'],
            $config['user'],
            $config['password'],
            '/',
            false,
            'AMQPLAIN',
            null,
            'en_US',
            3.0,
            3.0,
            $ssl_context
        );

看到没,verify_peer设置为false,是不是很眼熟,就是平时我们在https请求时忽略证书校验那样。至于AMQPConnectionConfig类设置参数这个我就不再细细研究了,原理是一样的。

verify_peer:验证证书是否对等,也就是是否需要校验证书的意思,设置为true就是要校验。

要校验就申请CA证书或者自签证书,放在参数里即可。

我这都没给我提供就懒得去折腾了,解决问题即可,遇到问题我们再解决。

相关推荐
初次攀爬者16 小时前
RabbitMQ的消息模式和高级特性
后端·消息队列·rabbitmq
初次攀爬者3 天前
ZooKeeper 实现分布式锁的两种方式
分布式·后端·zookeeper
让我上个超影吧4 天前
消息队列——RabbitMQ(高级)
java·rabbitmq
塔中妖4 天前
Windows 安装 RabbitMQ 详细教程(含 Erlang 环境配置)
windows·rabbitmq·erlang
断手当码农4 天前
Redis 实现分布式锁的三种方式
数据库·redis·分布式
初次攀爬者4 天前
Redis分布式锁实现的三种方式-基于setnx,lua脚本和Redisson
redis·分布式·后端
业精于勤_荒于稀4 天前
物流订单系统99.99%可用性全链路容灾体系落地操作手册
分布式
Ronin3054 天前
信道管理模块和异步线程模块
开发语言·c++·rabbitmq·异步线程·信道管理
Asher05094 天前
Hadoop核心技术与实战指南
大数据·hadoop·分布式
凉凉的知识库4 天前
Go中的零值与空值,你搞懂了么?
分布式·面试·go