laravel之rabbitmq组件使用

介绍

         laravel入门及技术指南,参见相关文章。laravel集成使用消息中间件rabbitmq,提供服务器异步消息处理,提升服务器性能。下面谈谈如何安装及集成使用。

安装

安装环境为centos7.6,php7.2.33

安装rabbitmq

//拉取镜像
# docker pull rabbitmq
//生成容器
# docker run -p 5672:5672 -p 15672:15672 -d --name rabbitmq -v /docker/rabbitmq:/var/lib/rabbitmq --privileged=true -e RABBITMQ_DEFAULT_VHOST=my_vhost -e RABBITMQ_DEFAULT_USER=admin -e RABBITMQ_DEFAULT_PASS=admin rabbitmq
//查看容器状态
# docker ps
//进入容器
# docker exec -it rabbitmq bash
进入容器内安装rabbitmq_management
rabbitmq-plugins enable rabbitmq_management 

 成功后,浏览器访问http://ip:15672/   登录 admin/admin

laravel之rabbitmq组件使用_第1张图片laravel之rabbitmq组件使用_第2张图片

安装php的rabbitmq扩展

查找、安装rabbitmq包

# yum search rabbitmq

# yum install -y librabbitmq.x86_64

安装php的amqp扩展

找到phpize,php编译工具

xxx/php/72/bin/phpize

准备素材包amqp-1.10.2.tgz、rabbitmq-c-0.8.0.tar.gz,放到/home下

 

//解压缩
home# tar zxvf amqp-1.10.2.tgz
home#  tar zxvf rabbitmq-c-0.8.0.tar.gz
//生成编译文件 configure
home/amqp-1.10.2# xxx/php/72/bin/phpize
home/rabbitmq-c-0.8.0#  xxx/php/72/bin/phpize
//这样amqp-1.10.2、rabbitmq-c-0.8.0下都生成了configure编译文件
编译安装
/home/rabbitmq-c-0.8.0#./configure --prefix=/home/rabbitmq-c-0.8.0
/home/rabbitmq-c-0.8.0# make && make install

home/amqp-1.10.2# ./configure --with-php-config=/www/server/php/72/bin/php-config --with-amqp --with-librabbitmq-dir=/home/rabbitmq-c-0.8.0
home/amqp-1.10.2# make && make install
成功后生成amqp.so,自动放在php扩展目录下,然后对相应php.ini中配置如下:
[rabbitmq]
extension = xxx/php/72/lib/php/extensions/no-debug-non-zts-20170718/amqp.so

我们查看php -m,看是否安装成功

laravel之rabbitmq组件使用_第3张图片

 

代码集成

laravel框架提供了rabbitmq集成

laravel需要composer安装的依赖包如下,采用laravels(laravel+swoole)提高性能。
"dingo/api": "^3.0",
"hhxsv5/laravel-s": "^3.7",
"vladimir-yuldashev/laravel-queue-rabbitmq": "10.X"

laravel配置rabbitmq

.env文件

...
#laravel默认连接为sync,改为rabbitmq 
QUEUE_CONNECTION=rabbitmq

#增加rabbitmq 
QUEUE_DRIVER=rabbitmq
RABBITMQ_HOST=172.17.0.3
RABBITMQ_PORT=5672
RABBITMQ_USER=admin
RABBITMQ_PASSWORD=admin
RABBITMQ_VHOST=my_vhost
RABBITMQ_QUEUE=lmrs

注: 上述172.17.0.3是rabbitmq安装后的容器ip。

 config/queue.php,修改增加rabbitmq配置

'rabbitmq' => [
            'driver' => 'rabbitmq',
            'queue' => env('RABBITMQ_QUEUE', 'default'),
            'connection' => PhpAmqpLib\Connection\AMQPLazyConnection::class,

            'hosts' => [
                [
                    'host' => env('RABBITMQ_HOST', '127.0.0.1'),
                    'port' => env('RABBITMQ_PORT', 5672),
                    'user' => env('RABBITMQ_USER', 'guest'),
                    'password' => env('RABBITMQ_PASSWORD', 'guest'),
                    'vhost' => env('RABBITMQ_VHOST', '/'),
                ],
            ],

            'options' => [
                'ssl_options' => [
                    'cafile' => env('RABBITMQ_SSL_CAFILE', null),
                    'local_cert' => env('RABBITMQ_SSL_LOCALCERT', null),
                    'local_key' => env('RABBITMQ_SSL_LOCALKEY', null),
                    'verify_peer' => env('RABBITMQ_SSL_VERIFY_PEER', true),
                    'passphrase' => env('RABBITMQ_SSL_PASSPHRASE', null),
                ],
                'queue' => [
                    'job' => \VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Jobs\RabbitMQJob::class,
                ]
            ],

            /*
             * Set to "horizon" if you wish to use Laravel Horizon.
             */
            'worker' => env('RABBITMQ_WORKER', 'default'),
        ]

 业务代码用例

服务类RabbitmqService:主要提供了rabbitmq服务的连接、推送到消息队列、取出消息等,这些基本方法。
services/RabbitmqService.php

class RabbitmqService{
    //取得rabbitmq连接
    public static function getConnect()    {
        $config = [
            'host' => env('RABBITMQ_HOST', '127.0.0.1'),
            'port' => env('RABBITMQ_PORT', 5672),
            'user' => env('RABBITMQ_USER', 'guest'),
            'password' => env('RABBITMQ_PASSWORD', 'guest'),
            'vhost' => env('RABBITMQ_VHOST', '/'),
        ];

        return new AMQPStreamConnection($config["host"],$config["port"],$config["user"],$config["password"],$config["vhost"]);
    }
//推送到消息队列
    public static function push($queue,$messageBody,$exchange='router')    {
        $connection = self::getConnect();
        $channel = $connection->channel();
        //声明一个队列
        $channel->queue_declare($queue,false,true,false,false);
        $channel->exchange_declare($exchange,'direct',false,true,false);
        $channel->queue_bind($queue,$exchange);
        $message = new  AMQPMessage($messageBody,array('content_type' => 'text/plain', 'delivery_mode' => AMQPMessage::DELIVERY_MODE_PERSISTENT));
        $channel->basic_publish($message,$exchange);
        $channel->close();
        $connection->close();
    }
//取出消息
    public static function pop($queue,$callback,$exchange='router')
    {
        $connection = self::getConnect();
        $channel = $connection->channel();
        $message = $channel->basic_get($queue);
        $res = $callback($message->getBody());
        if ($res){
            $channel->basic_ack($message->getDeliveryTag());
        }
        $channel->close();
        $connection->close();
    }
}

消息处理类:主要将消息通过调用上述消息服务连接、推送、取出基本接口调用,进行消息的处理。

Jobs/UpdateProduct.php,本例给出一个获取消息,推送消息队列,并在redis缓存消息表示消费消息,rabbitmq的内部ack应答机制保证消息可靠,保证在执行消息读取后处理(这是只是redis缓存当作处理)能完整完成,不会因故障导致消息丢失。

class UpdateProduct implements ShouldQueue{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    protected  $productKey;

    /**
     * Create a new job instance.
     * 构造方法传入数据消息,生成消息键值,并放入消息队列
     * @return void
     */
    public function __construct($data)
    {
        $this->productKey = "Lmrs::product::info::".$data->id;

        RabbitmqService::push('update_queue',$data);
    }

    /**
     * Execute the job.
     * 取出消息的处理
     * @return void
     */
    public function handle()
    {
        RabbitmqService::pop('update_queue',function ($message){
            $product = app('redis')->set($this->productKey,serialize($message));
            if (!$product){
                return;
            }
            print_r($message);
        });
    }
//异常或失败的处理
    public function failed(\Exception $exception)   {
        print_r($exception->getMessage());
    }
}

业务产生消息的控制类:业务中产生消息,并调用上述消息处理器,进入rabbitmq服务器。

ProductController.php。本例模拟一个更新商品名称信息的简单用例,将更新消息推送给rabbitmq进行处理。

class ProductController extends Controller{
...
    //测试消息中间件rabbitmq
    public function update(Request $request)   {
        $product = Product::find($request->input("id"));
        $product->update([
            "name" => $request->input("name"),
            "long_name" => $request->input("long_name")
        ]);
        //return Product::find($request->input("id"));
        if ($product){
            $this->dispatch(new UpdateProduct(Product::find($request->input("id"))));
            return "success";
        }
        return "Failed";
    }
}

运行测试

测试

我们用postman工具,模拟发送请求

laravel之rabbitmq组件使用_第4张图片

 

消息监听

laravel框架提供rabbitmq监听工具,上述配置好laravel的rabbitmq相关参数,运行下面

# php artisan queue:work rabbitmq

注:有时会报错“pcntl_signal() has been disabled for security reasons”等,需要取消php的禁用函数pcntl_signal、pcntl_alarm

运行正常后,接到发来的消息,显示如下:

rabbitmq服务器查看消息

laravel之rabbitmq组件使用_第5张图片

 

你可能感兴趣的:(基础框架技术,性能优化)