Docker+TP5+RabbitMQ+入消息队列+自动消费队列

注意:仅仅记录学习,不能直接运行,有任何问题请留言。

1. 安装RabbitMQ

拉取镜像

docker pull rabbitmq:3.7.7-management
docker run -d --name rabbitmq3.7.7 -p 5672:5672 -p 15672:15672 -v `pwd`/data:/var/lib/rabbitmq --hostname myRabbit -e RABBITMQ_DEFAULT_VHOST=my_vhost  -e RABBITMQ_DEFAULT_USER=admin -e RABBITMQ_DEFAULT_PASS=admin df80af9ca0c9

http://ip:15672

image.png

2. composer安装php-amqplib

composer require php-amqplib/php-amqplib

3.Tp5 实现

再次封装php-amqplib

<?php
/**
 * User: yuzhao
 * Description: RabbitMq 工具
 */
namespace app\common\tool;
use app\common\config\SelfConfig;
use PhpAmqpLib\Connection\AMQPStreamConnection;
use PhpAmqpLib\Message\AMQPMessage;

class RabbitMQTool {

    /**
     * User: yuzhao
     * @var
     * Description:
     */
    private $channel;

    private $mqConf;

    /**
     * RabbitMQTool constructor.
     * @param $mqName
     */
    public function __construct($mqName)
    {
        // 获取rabbitmq所有配置
        $rabbitMqConf = SelfConfig::getConfig('Source.rabbit_mq');
        if (!isset($rabbitMqConf['rabbit_mq_queue'])) {
            die('没有定义Source.rabbit_mq');
        }
        //建立生产者与mq之间的连接
        $this->conn = new AMQPStreamConnection(
            $rabbitMqConf['host'], $rabbitMqConf['port'], $rabbitMqConf['user'], $rabbitMqConf['pwd'], $rabbitMqConf['vhost']
        );
        $channal = $this->conn->channel();
        if (!isset($rabbitMqConf['rabbit_mq_queue'][$mqName])) {
            die('没有定义'.$mqName);
        }
        // 获取具体mq信息
        $mqConf = $rabbitMqConf['rabbit_mq_queue'][$mqName];
        $this->mqConf = $mqConf;
        // 声明初始化交换机
        $channal->exchange_declare($mqConf['exchange_name'], 'direct', false, true, false);
        // 声明初始化一条队列
        $channal->queue_declare($mqConf['queue_name'], false, true, false, false);
        // 交换机队列绑定
        $channal->queue_bind($mqConf['queue_name'], $mqConf['exchange_name']);
        $this->channel = $channal;
    }

    /**
     * User: yuzhao
     * @param $mqName
     * @return RabbitMQTool
     * Description: 返回当前实例
     */
    public static function instance($mqName) {
        return new RabbitMQTool($mqName);
    }

    /**
     * User: yuzhao
     * @param $data
     * Description: 写mq
     * @return bool
     */
    public function wMq($data) {
        try {
            $data = json_encode($data, JSON_UNESCAPED_UNICODE);
            $msg = new AMQPMessage($data, ['content_type' => 'text/plain', 'delivery_mode' => 2]);
            $this->channel->basic_publish($msg, $this->mqConf['exchange_name']);
        } catch (\Throwable $e) {
            $this->closeConn();
            return false;
        }
        $this->closeConn();
        return true;
    }

    /**
     * User: yuzhao
     * @param int $num
     * @return array
     * Description:
     * @throws \ErrorException
     */
    public function rMq($num=1) {
        $rData = [];
        $callBack = function ($msg) use (&$rData){
            $rData[] = json_decode($msg->body, true);
        };
        for ($i=0;$i<$num;$i++) {
            $this->channel->basic_consume($this->mqConf['queue_name'], '', false, true, false, false, $callBack);
        }
        $this->channel->wait();
        $this->closeConn();
        return $rData;
    }

    /**
     * User: yuzhao
     * Description: 关闭连接
     */
    public function closeConn() {
        $this->channel->close();
        $this->conn->close();
    }

}

入队列

<?php
/**
 * User: yuzhao
 * Description:
 */
namespace app\test\controller;

use app\common\tool\RabbitMQTool;
use think\Controller;

class TestController extends Controller {
    public function test() {
        RabbitMQTool::instance('test')->wMq(['name'=>'123']);
    }
}

启动消费队列

<?php
/**
 * User: yuzhao
 * Description: 启动MQ,php xxx/public/index.php /daemon/start_Mq/main 队列别名 进程数 -d(守护进程) | -s (杀死进程)
 */

namespace app\daemon\controller;
use app\common\config\SelfConfig;
use app\common\tool\RabbitMQTool;

class StartMqController {

    private $dealPath = null;

    private $childsPid = array();

    /**
     * StartRabbitMQ constructor.
     */
    public function __construct()
    {
        // 脚本路径
        $this->dealPath = str_replace('/','\\',"/app/daemon/deal/");
    }

    /**
     * User: yuzhao
     * Description: 返回当前实例
     */
    public static function instance() {
        return new StartMqController();
    }

    /**
     * User: yuzhao
     * Description: 主要处理流程
     * @throws \ErrorException
     */
    public function main() {
        global $argv;
        // 扩展参数
        if (isset($argv[3])) {
            switch ($argv[3]) {
                case '-d': // 守护进程启动
                    $this->daemonStart();
                break;
                case '-s': // 杀死进程
                    $this->killEasyExport($argv[2]);die();
                break;
            }
        }
        // 判断参数
        if (count($argv) < 2) {
            die('缺少参数');
        }
        // 获取配置信息
        $rabbitMqConf = SelfConfig::getConfig('Source.rabbit_mq');
        if (!isset( $rabbitMqConf['rabbit_mq_queue'][$argv[2]])) {
            die('没有配置:'.$argv[2]);
        }
        // 获取mq配置
        $mqConf = $rabbitMqConf['rabbit_mq_queue'][$argv[2]];
        // 实例化处理脚本
        $dealClass = $this->dealPath.$mqConf['consumer'];
        $dealObj = new $dealClass;
        $processNum = 1;
        if (isset($mqConf['process_num']) || !is_numeric($mqConf['process_num']) || $mqConf['process_num'] < 1 || $mqConf['process_num'] >10 ) {
            $processNum = $mqConf['process_num'];
        }
        if (!isset($mqConf['deal_num']) || !is_numeric($mqConf['deal_num'])) {
            die('处理条数设置有误');
        }
        // fork进程
        for ($i=0; $i<$processNum; $i++) {
            $pid = pcntl_fork();
            if( $pid < 0 ){
                exit();
            } else if( 0 == $pid ) {
                $this->downMqData($dealObj, $argv, $mqConf);
                exit();
            } else if( $pid > 0 ) {
                $this->childsPid[] = $pid;
            }
        }
        while( true ){
            sleep(1);
        }
    }

    /**
     * User: yuzhao
     * @param $dealObj
     * @param $argv
     * @param $mqConf
     * @throws \ErrorException
     * Description:
     */
    private function downMqData($dealObj, $argv, $mqConf) {
        while (true) {
            // 下载数据
            $mqData = RabbitMQTool::instance($argv[2])->rMq($mqConf['deal_num']);
            $dealObj->deal($mqData);
            sleep(1);
        }
    }

    private function killEasyExport($startFile) {
        exec("ps aux | grep $startFile | grep -v grep | awk '{print $2}'", $info);
        if (count($info) <= 1) {
            echo "not run\n";
        } else {
            echo "[$startFile] stop success";
            exec("ps aux | grep $startFile | grep -v grep | awk '{print $2}' |xargs kill -SIGINT", $info);
        }
    }

    /**
     * User: yuzhao
     * Description: 守护进程模式启动
     */
    private function daemonStart() {
        // 守护进程需要pcntl扩展支持
        if (!function_exists('pcntl_fork'))
        {
            exit('Daemonize needs pcntl, the pcntl extension was not found');
        }
        umask( 0 );
        $pid = pcntl_fork();
        if( $pid < 0 ){
            exit('fork error.');
        } else if( $pid > 0 ) {
            exit();
        }
        if( !posix_setsid() ){
            exit('setsid error.');
        }
        $pid = pcntl_fork();
        if( $pid  < 0 ){
            exit('fork error');
        } else if( $pid > 0 ) {
            // 主进程退出
            exit;
        }
        // 子进程继续,实现daemon化
    }

}

自定义配置文件

<?php
/**
 * User: yuzhao
 * Description:
 */

return [
   
    'rabbit_mq' => [
        'host' => ip,
        'port' => 5672,
        'user' => 'root',
        'pwd' => 'xxx',
        'vhost' => 'my_vhost',
        'rabbit_mq_queue' => [
            'test' => [
                'exchange_name' => 'ex_test', // 交换机名称
                'queue_name' => 'que_test', // 队列名称
                'process_num' => 3, // 默认单台机器的进程数量
                'deal_num' => '50', // 单次处理数量
                'consumer' => 'DealTest' // 消费地址
            ]
        ]
    ]
];

4. 学习地址

https://www.cnblogs.com/yufeng218/p/9452621.html
https://blog.csdn.net/demon3182/article/details/77335206
https://blog.csdn.net/u010472499/article/details/78366614
https://segmentfault.com/a/1190000012308675

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,298评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,701评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,078评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,687评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,018评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,410评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,729评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,412评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,124评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,379评论 2 242
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,903评论 1 257
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,268评论 2 251
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,894评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,014评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,770评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,435评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,312评论 2 260

推荐阅读更多精彩内容