Laravel 8.63.0 之 RabbitMQ 生产&消费案例

场景:微服务中,会遇到这样的案例:用户申请提现,总后台(后台服务)审核通过,通知资金服务 更新数据;

1.安装 composer 包
composer requires php-amqplib/php-amqplib ^2.12
2. env 追加配置
RABBITMQ_HOST=xb_rabbitmq
RABBITMQ_PORT=5672
#通过15672创建的rabbitmq虚拟主机,默认是'/'
RABBITMQ_VHOST=/
RABBITMQ_USER=guest
RABBITMQ_PASSWORD=guest
#通过15672创建的rabbitmq队列
RABBITMQ_QUEUE=withdrawal-queue
RABBITMQ_EXCHANGE=withdrawal-exchange


QUEUE_CONNECTION=rabbitmq # 更新
3.追加配置 config/queue.php connections 下 后 执行 php artisan config:cache
'rabbitmq' => [
            'driver'                => 'rabbitmq',

            'host'                  => env('RABBITMQ_HOST', '127.0.0.1'),
            'port'                  => env('RABBITMQ_PORT', 5672),

            'vhost'                 => env('RABBITMQ_VHOST', '/'),
            'login'                 => env('RABBITMQ_LOGIN', 'guest'),
            'password'              => env('RABBITMQ_PASSWORD', 'guest'),

            'queue'                 => env('RABBITMQ_QUEUE'), // name of the default queue,
            'exchange_name'         => env('RABBITMQ_QUEUE'),

            'exchange_declare'      => env('RABBITMQ_EXCHANGE_DECLARE', true), // create the exchange if not exists
            'queue_declare_bind'    => env('RABBITMQ_QUEUE_DECLARE_BIND', true), // create the queue if not exists and bind to the exchange

            'queue_params'          => [
                'passive'           => env('RABBITMQ_QUEUE_PASSIVE', false),
                'durable'           => env('RABBITMQ_QUEUE_DURABLE', true),
                'exclusive'         => env('RABBITMQ_QUEUE_EXCLUSIVE', false),
                'auto_delete'       => env('RABBITMQ_QUEUE_AUTODELETE', false),
            ],

            'exchange_params' => [
                'name'        => env('RABBITMQ_EXCHANGE_NAME', null),
                'type'        => env('RABBITMQ_EXCHANGE_TYPE', 'direct'), // more info at http://www.rabbitmq.com/tutorials/amqp-concepts.html
                'passive'     => env('RABBITMQ_EXCHANGE_PASSIVE', false),
                'durable'     => env('RABBITMQ_EXCHANGE_DURABLE', true), // the exchange will survive server restarts
                'auto_delete' => env('RABBITMQ_EXCHANGE_AUTODELETE', false),
            ],

        ],
4.审核通过方法
    public function adopt()
    {
        //我将用户提现申请,审核通过 我生产一条消息 等待资金服务消费去做资金改动
        $withdrawalId = 100;//提现id
        event(new WithdrawalEvent(['withdrawal_id' => $withdrawalId]));
    }

5.创建 监听事件 Event
<?php

namespace App\Events;

use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;

class WithdrawalEvent
{
    use Dispatchable, InteractsWithSockets, SerializesModels;

    public $withdrawal;
    public function __construct($withdrawal)
    {
        $this->withdrawal= $withdrawal;
    }

    /**
     * Get the channels the event should broadcast on.
     *
     * @return \Illuminate\Broadcasting\Channel|array
     */
    public function broadcastOn()
    {
        return new PrivateChannel('channel-name');
    }
}


5.创建 监听事件 Listener
<?php

namespace App\Listeners;

use App\Events\WithdrawalEvent;
use Illuminate\Support\Facades\Log;
use PhpAmqpLib\Connection\AMQPStreamConnection;
use PhpAmqpLib\Message\AMQPMessage;

class WithdrawalListener
{
    protected $config = [];
    public function __construct()
    {
        $this->config = config('queue.connections.rabbitmq');
    }

    /**
     * Handle the event.
     *
     * @param  WithdrawalEvent  $event
     * @return void
     */
    public function handle(WithdrawalEvent $event)
    {
        try {
            $connect = new AMQPStreamConnection( //建立生产者与mq之间的连接
                $this->config['host'],$this->config['port'],$this->config['login'],$this->config['password'], '/'
            );
            $channel = $connect->channel(); //在已连接基础上建立生产者与mq之间的通道
            $channel->exchange_declare($this->config['exchange_name'], 'direct', false, true, false); //声明初始化交换机
            $channel->queue_declare($this->config['queue'], false, true, false, false); //声明初始化一条队列
            $channel->queue_bind($this->config['queue'], $this->config['exchange_name']); //将队列与某个交换机进行绑定,并使用路由关键字
            $msgBody = json_encode($event->withdrawal);
            $msg = new AMQPMessage($msgBody, ['content_type' => 'text/plain', 'delivery_mode' => 2]); //生成消息
            $channel->basic_publish($msg, $this->config['exchange_name']); //推送消息到某个交换机
            $channel->close();
            $connect->close();
        }catch (\Exception $exception){
            Log::info($exception->getMessage());
        }
    }
}
6.创建服务层
<?php

namespace App\Services;

use Illuminate\Support\Facades\Log;
use PhpAmqpLib\Connection\AMQPStreamConnection;
use PhpAmqpLib\Message\AMQPMessage;

class RmqClientService
{
    /**
     * @var object $instance 单例对象
     */
    private static $instance = null;

    /**
     * @var object $connection 队列连接对象
     */
    private $connection = null;

    /**
     * @var object $channel 队列通道对象
     */
    private $channel = null;

    /**
     * @var object $message 队列消息对象
     */
    private $message = null;

    /**
     * 构造函数
     *
     */
    private function __construct()
    {
        //dd(config('queue.connections.rabbitmq.vhost'));
        $this->connection = new AMQPStreamConnection(
            config('queue.connections.rabbitmq.host'),
            config('queue.connections.rabbitmq.port'),
            config('queue.connections.rabbitmq.login'),
            config('queue.connections.rabbitmq.password'),
            config('queue.connections.rabbitmq.vhost')
        );
        $this->channel = $this->connection->channel();
        $this->message = new AMQPMessage('', ['content_type' => 'json', 'delivery_mode' => AMQPMessage::DELIVERY_MODE_PERSISTENT]);
    }

    /**
     * FunctionName:__clone
     * Description:克隆
     * Author:lwl
     */
    private function __clone()
    {}

    /**
     * 析构函数
     */
    public function __destruct()
    {
        $this->channel->close();
        $this->connection->close();
        self::$instance = null;
    }

    /**
     * FunctionName:getInstance
     * Description:单例实例化入口
     * Author:lwl
     * @return RmqClientService|object|null
     */
    public static function getInstance()
    {
        if (!self::$instance instanceof self) {
            self::$instance = new self();
        }
        return self::$instance;
    }

    /**
     * FunctionName:consumer
     * Description:消费队列
     * Author:lwl
     * @param string $queue 队列名称
     * @param boolean $bForceDelete 是否取后即删
     * @return AMQPMessage|null
     */
    public function consumer(string $queue, bool $bForceDelete = false)
    {
        try {
            // 取数据
            // 声明队列
            // 不检测同名队列,持久化,不允许其他队列访问,不自动删除队列
            $this->channel->queue_declare($queue, false, true, false, false);
            $message = $this->channel->basic_get($queue);
            if ($message && $bForceDelete) {
                // 回复确认信息
                $this->channel->basic_ack($message->delivery_info['delivery_tag']);
            }
        } catch (\Exception $exception) {
            Log::info($exception->getMessage());
        }
        return $message;
    }

    /**
     * FunctionName:ack
     * Description:
     * Author:lwl
     * @param $nTag 消息传递标签
     * @return mixed
     */
    public function ack($nTag)
    {
        try {
            $this->channel->basic_ack($nTag);
        } catch (\Exception $e) {
            dd($e->getMessage());
        }
        return null;//success 个人响应
    }
}

7.创建 Command
<?php
namespace App\Console\Commands;

use App\Services\RmqClientService;
use Illuminate\Console\Command;

class WithdrawalConsumerCommand extends Command
{
    protected $signature = 'withdrawal:consumer';
    protected $description = '消费提现审核通过后的消息';

    public function handle()
    {
        while (true) {
            $service = RmqClientService::getInstance();
            $queue = config('queue.connections.rabbitmq.queue');
            $response = $service->consumer($queue, true);
            if ($response) {
                $result = json_decode($response->body,1);
                dd($result);
                //资金服务后续操作
            }
            dd('service error');
        }
    }
}

8.测试
1.通过 ‘提现审核通过‘ 的路由 生产消息
	http://test.test/api/user/adopt
	
2.执行 php artisan withdrawal:consumer #如下图 生产环境使用 Supvervisor 等进程管理 常驻监听 (请查看 10)

在这里插入图片描述

规则说明
direct精准推送
fanout 广播推送到绑定到此交换机下的所有队列
topic 组播比如上面我绑定的关键字是sms_send,那么他可以推送到*.sms_send的所有队列
headers这个目前不知道是如何推送的
9.在创建交换机和队列的时候各个常用参数说明 地址


   name: $queue    // should be unique in fanout exchange. [队列名称]
   passive: false  // don't check if a queue with the same name exists [是否检测同名队列]
   durable: false // the queue will not survive server restarts [是否开启队列持久化]
   exclusive: false // the queue might be accessed by other channels [队列是否可以被其他队列访问]
   auto_delete: true //the queue will be deleted once the channel is closed. [通道关闭后是否删除队列]
   
   
   
   name: $exchange [交换机名称]
   type: direct [路由类型]
   passive: false []
   durable: true [交换机是否开启持久化]
   auto_delete: false //the exchange won't be deleted once the channel is closed.
10.Supvervisor 守护进程 消费 ,进入容器内

10-1.安装 supervisor

apt-get install supervisor

10-2.切换到配置目录

cd /etc/supervisor/conf.d

10-3.写入配置 vim mq.conf

[program:withdrawal_consumer]                                       #管理进程的命名
   command=php artisan withdrawal:consumer     #执行的命令
   stderr_logfile=/var/log/supervisor/error.log      #错误日志输出路径
   stdout_logfile=/var/log/supervisor/supervisor.log   #日志输出路径
   directory=/workspace/xiaoba/oldLiuCms      #命令执行的工作空间
   autostart=true                 #自动启动
   user=root                   #指定用户
   autorestart=true                #自动重启

10-4.重新加载

service  supervisor   force-reload

10-5.启动进程

 service  supervisor   start withdrawal_consumer
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
对于线性规划求解算法,常用的方法有单纯形法、内点法和对偶法等。其中,单纯形法是最常用的方法之一。 模型建立的最小化目标函数为 Zmin = ∑2ai∕b∣X-0.307i∣ ,约束条件如下: - 河道长为 8.63 km(桩号 18+625~27+250) - 各料场土料的最大干密度为 1.62~1.78 g/cm3 - 开挖土料采用 8~15t 自卸车运输,所以 4.49 ≤ b ≤ 8.43 - ai 为每个单元的挖土量,共有 28 组数据 - b 为运输车辆装卸载量,且 4.49 ≤ b ≤ 8.43 - n 为河道划分单元总数为 28 要求求解 X 为何值时 Z 最小,并以图形方式表示并给出对应的 MATLAB 代码。 这是一个线性规划问题,我们可以通过 MATLAB 的优化工具箱来求解。下面是相应的 MATLAB 代码: ```matlab % 定义目标函数系数向量 c c = [2 * ai ./ b; -2 * ai ./ b]; % 定义线性不等式约束矩阵 A 和约束向量 b A = [-eye(28), zeros(28, 28); eye(28), zeros(28, 28)]; b = [zeros(28, 1); ones(28, 1)]; % 定义线性等式约束矩阵 Aeq 和约束向量 beq Aeq = [zeros(1, 28), ones(1, 28)]; beq = 1; % 定义变量的上下界 lb = zeros(56, 1); ub = ones(56, 1); % 使用线性规划函数 linprog 求解最小化问题 [x, fval] = linprog(c, A, b, Aeq, beq, lb, ub); % 提取 X 的值 X = sum(x(29:end) .* (0.307:0.307:8.63)); disp(['X = ', num2str(X)]); % 绘制图形 figure; plot(0.307:0.307:8.63, x(29:end)); xlabel('X'); ylabel('Z'); ``` 这段代码中,c 是目标函数系数向量,A 和 b 是线性不等式约束矩阵和约束向量,Aeq 和 beq 是线性等式约束矩阵和约束向量,lb 和 ub 是变量的上下界。使用 linprog 函数求解后,可以得到最小化问题的最优解 x 和目标函数值 fval。通过提取 x 中对应的值,即可得到 X 的值,并使用 plot 函数绘制出对应的图形。 请注意,这段代码中的 ai 和 b 的值需要根据具体数据进行替换,保持相应的维度和顺序。同时,也需要确保 MATLAB 中已经安装了优化工具箱。 希望这能对你有所帮助!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值