PHP + RabbitMQ的消息发布和消费

环境:centos7

Centos7上面安装rabbitmq工具
1、安装erlang:yum install -y epel-release erlang

2、查看版本:erl -version

3、下载rabbitmq-server并进行yum安装,官网:http://www.rabbitmq.com/download.html
(3-1)cd到src目录:cd /usr/src
(3-2)下载最新的:
wget https://github.com/rabbitmq/rabbitmq-server/releases/download/v3.9.8/rabbitmq-server-3.9.8-1.el7.noarch.rpm
(3-3)然后执行:rpm --import http://www.rabbitmq.com/rabbitmq-signing-key-public.asc
(3-4)安装:yum install -y rabbitmq-server
(3-5)启动
开机自启动:chkconfig rabbitmq-server on
启动服务:service rabbitmq-server start
查看服务状态:service rabbitmq-server status
(3-6)安装web管理工具:rabbitmq-plugins enable rabbitmq_management

4.安装完成,默认账号:guest,密码 :guest,添加可以远程登陆的账号
sudo rabbitmqctl add_user test 123456
sudo rabbitmqctl set_user_tags test administrator
sudo rabbitmqctl set_permissions -p / test “." ".” “.*”
浏览器访问:服务器ip+端口15672
访问不了则需要设置防火墙,开启15672端口,(直接关闭防火墙不行),操作参考如下(参考(4-1)开放端口即可):
(4-1)开放端口
firewall-cmd --zone=public --add-port=15672/tcp --permanent #开放15672端口
firewall-cmd --zone=public --remove-port=15672/tcp --permanent #关闭15672端口
firewall-cmd --reload #配置立即生效

(4-2)查看防火墙所有开放的端口:firewall-cmd --zone=public --list-ports

(4-3)关闭防火墙:systemctl stop firewalld.service ,如果要开放的端口太多,嫌麻烦,可以关闭防火墙,安全性自行评估

(4-4)查看防火墙状态:firewall-cmd --state

(4-5)查看监听的端口:netstat -lnpt
PS:centos7默认没有 netstat 命令,需要安装 net-tools 工具,yum install -y net-tools

(4-6)检查端口被哪个进程占用:netstat -lnpt |grep 5672

(4-7)查看进程的详细信息:ps 6832

(4-8)中止进程:kill -9 6832

5.以上操作成功后,在服务器项目中使用composer安装:php-amqplib/php-amqplib
命令:composer require php-amqplib/php-amqplib
安装过程遇到的问题
(5-1)如果有提示让运行带上–no-plugins的,有的话执行下面第一个操作先看看
使用:composer selfupdate --no-plugins
(5-2)如果有还有其他的提示,试试以下操作
先删除tp框架中的:composer.lock文件
清除缓存:composer clearcache
更新:composer update 或者 composer self-update

生产者和消费者之间的逻辑代码:

项目config文件夹下新增配置文件rabbitmq.php

<?php
return [
    # 连接信息
    'AMQP' => [
        'host' => '192.168.2.198', //队列服务器ip
        'port' => '5672',          //默认端口
        'login' => 'root',         //账号,步骤4中添加
        'password' => '123456',    //密码
        'vhost' => '/'
    ],
    # 邮件队列
    'email_queue' => [
        'exchange_name' => 'email_exchange',
        'exchange_type' => 'direct',              #直连模式
        'queue_name' => 'email_queue',
        'route_key' => 'email_roteking',
        'consumer_tag' => 'consumer'
    ]
];

生产者:

<?php
namespace app\service\controller;

use PhpAmqpLib\Connection\AMQPStreamConnection;
use PhpAmqpLib\Message\AMQPMessage;

class Publisher
{
    public function pushMessage()
    {
        $data = 'time:'.time().' your mother call you back home ';
        $param = config('rabbitmq.AMQP');
        $amqpDetail = config('rabbitmq.email_queue');
        $connection = new AMQPStreamConnection(
            $param['host'],
            $param['port'],
            $param['login'],
            $param['password'],
            $param['vhost']
        );
        $channel = $connection->channel();
        /*
             name: $queue  创建队列
             passive: false
             持久durable: true // //队列将在服务器重启后继续存在
             互斥exclusive: false // 队列可以通过其他渠道访问
             auto_delete: false 通道关闭后,队列不会被删除
         */
        $channel->queue_declare($amqpDetail['queue_name'], false, true, false, false);

        /*
            name: $exchange  创建交换机
            type: direct   直连方式
            passive: false
            durable: true  持久// 交换器将在服务器重启后继续存在
            auto_delete: false //一旦通道关闭,交换器将不会被删除。
        */
        $channel->exchange_declare($amqpDetail['exchange_name'], 'direct', false, true, false);

        /*
             $messageBody:消息体
             content_type:消息的类型 可以不指定
             delivery_mode:消息持久化最关键的参数
             AMQPMessage::DELIVERY_MODE_NON_PERSISTENT = 1;
             AMQPMessage::DELIVERY_MODE_PERSISTENT = 2;
         */
        $messageBody = $data;
        $message = new AMQPMessage($messageBody, array('content_type' => 'text/plain', 'delivery_mode' => AMQPMessage::DELIVERY_MODE_PERSISTENT));
        $channel->basic_publish($message, $amqpDetail['exchange_name'],$amqpDetail['route_key']);
        $channel->close();
        $connection->close();
        echo  "ok";
    }
}

消费者:

<?php
namespace app\service\controller;

use PhpAmqpLib\Connection\AMQPStreamConnection;
use think\facade\Log;

class Consumer
{
    /**
     * 消费端 消费端需要保持运行状态实现方式
     * 1 linux上写定时任务每隔5分钟运行下该脚本,保证访问服务器的ip比较平缓,不至于崩溃
     * 2 nohup php index.php index/Message_Consume/start &  用nohup命令后台运行该脚本
     * 3
     **/
    function shutdown($channel, $connection)
    {
        $channel->close();
        $connection->close();
        Log::write("closed",3);
    }

    function process_message($message)
    {
        echo  $message->body."\n";

        //手动发送ack
        $message->delivery_info['channel']->basic_ack($message->delivery_info['delivery_tag']);
        // Send a message with the string "quit" to cancel the consumer.
        if ($message->body === 'quit') {
            $message->delivery_info['channel']->basic_cancel($message->delivery_info['consumer_tag']);
        }
    }

    /**
     * 启动
     * @return \think\Response
     */
    public function start()
    {
        $param = config('rabbitmq.AMQP');
        $amqpDetail = config('rabbitmq.email_queue');
        $connection = new AMQPStreamConnection(
            $param['host'],
            $param['port'],
            $param['login'],
            $param['password'],
            $param['vhost']
        );
        $channel = $connection->channel();
        $channel->queue_declare($amqpDetail['queue_name'], false, true, false, false);
        $channel->exchange_declare($amqpDetail['exchange_name'], 'direct', false, true, false);
        $channel->queue_bind($amqpDetail['queue_name'],                $amqpDetail['exchange_name'],$amqpDetail['route_key']);

        /*
            queue: 从哪里获取消息的队列
            consumer_tag: 消费者标识符
            no_local: 不接收此使用者发布的消息
            no_ack: 如果求设置为true,则此使用者将使用自动确认模式。详情请参见.
            exclusive:请独占使用者访问,这意味着只有这个使用者可以访问队列
            nowait:
            callback: :PHP回调 array($this, 'process_message') 调用本对象的process_message方法
        */

        $channel->basic_consume($amqpDetail['queue_name'], $amqpDetail['consumer_tag'], false, false, false, false, array($this, 'process_message'));
        register_shutdown_function(array($this, 'shutdown'), $channel, $connection);

        $count = count($channel->callbacks);
        while ($count) {
            $channel->wait();
        }
    }
}

完成生产者和消费者的两个类,下面我们把我们先自定义一个命令行,然后用这个命令:php think make:command Consumer行执行我们的消费者去消费消息生成命令行,然后稍作修改
执行完成后,在application目录下面会自动生成一个command目录,打开里面的Consumer.php文件,修改参考下面内容

protected function configure()
{
    // 指令配置
    $this->setName('consumer')->setDescription('php think consumer');
}

protected function execute(Input $input, Output $output)
{
    $consumer = new Consumer();
   // 指令输出
    $consumer->start();
}

另外:tp6和tp5.1config目录下面的console.php文件内容各不相同

tp6:添加如下内容,注意如果在linux系统里,app\Command\Consumer,会提示找不到,有大小写区分(tp5.1忽略此项)

<?php

return [
    // 指令定义
    'commands' => [
        // consumer是app\Command\Consumer文件中自定义命令行的名字
        'consumer' => 'app\Command\Consumer'
    ],
];

tp5.1:是自带的默认内容,不需要修改

<?php

return [
    'name'      => 'Think Console',
    'version'   => '0.1',
    'user'      => null,
    'auto_path' => env('app_path') . 'command' . DIRECTORY_SEPARATOR,
];

然后在项目中使用命令:php think consumer ,将消费者端运行挂起

完成上述内容后,写一个接口,模拟用户访问:

<?php

namespace app\service\controller;

class MqService
{
    public function send()
    {
        $consumer = new Publisher();//生产者
        $consumer->pushMessage('time:'.time().' Look at the girl opposite ');
    }
}

然后访问浏览器或者postman访问send:http://127.0.0.1/service/MqService/send
此时就会看到挂起消费端的控制台,会输出“Look at the girl opposite”,则表示生产者发出的消息,消费者已经将其消费掉了
如果关掉消费端的挂起,再次访问send方法,在浏览器访问:服务器ip+端口15672的页面列,队列项中就会看到未被消费的消息

参考:https://blog.csdn.net/qq_39077464/article/details/108993831

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值