How to ensure data security rabbimq

table of Contents

Scratch. Endurance of

Two. ACK

Third Transaction:

Confirm mode

Outline

The principle of producer end confirm mode

Open confirm mode method


Scratch. Endurance of

RabbitMQ supports message persistence, that is, data is written to disk.

(1) Exchange persistent, durable specified when declaring => 1

(2) queue persistent, durable specified when declaring => 1

(3) persistent message, when delivering specified delivery_mode => 2 (a non-persistent)

Note: If the message persistence, queue is not persistent, restart the service message will still be lost, but not persistent exchange will not be affected, so while persistent messages must be persistent queue;


Two. ACK

The above guarantee the persistence queue of data in case of server downtime are not lost restart, but when the process of removing the consumer message processing in a queue fails, the message server abnormalities lead to failure, which can lead to data lost, ACK mechanism is to solve such problems,

In RabbitMQ, the acknowledgment message has two modes:

1. Automatic mode, we do not need any action, after receiving the message is the consumer, will be automatically confirmed, the message will be deleted from the queue.

2. Manual mode, the message is the consumer, we need to call the API RabbitMQ provide to fulfill the message confirmation.

Code:

/**
 * 测试Ack消费
 *
 * @throws \ErrorException
 * @date 2019-06-18
 */
public function actionConsumeMessageAck()
{
    $channel = RabbitmqBase::createChannel();
    $callback = function ($msg) {
        echo " [x] Received ", $msg->body, "\n";
        var_dump($msg->delivery_info['delivery_tag']);//队列中每个消息的标记
        sleep(1);
        //处理消息之后确认(一定要确定开启了消息确认机制才能用这个)
        $msg->delivery_info['channel']->basic_ack($msg->delivery_info['delivery_tag']);
    };

    //可告知RabbitMQ只有在consumer处理并确认了上一个message后才分配新的message给他,否则上一个没处理完会一直卡在这里,这个根据业务场景配置
    //basic_qos($prefetch_size,$prefetch_count, //最重要的参数,未确认的消息同时存在的个数;(也就是未ack的消息数,我们可以以此来作为记录失败数据的个数;)$a_global)
    $channel->basic_qos(null, 1, null);//取数据时按优先级取,但是取到的数据消费时按顺序消费,没有优先级概念
    //no_ack为false(默认)开启确认机制
    $channel->basic_consume('AQ1', '', false, false, false, false, $callback);

    while (count($channel->callbacks)) {
        $channel->wait();//这个在没有消息会等待,有消息就会自动运行
    }
    $channel->close();
    RabbitmqBase::closeConnection();
}

See above section of code requires manual confirmation basic_consume when the fourth parameter is set to fasle, the queue will be deleted after the acknowledgment message only, if the automatic mode, as long as the data is removed, it will delete the data queue,

Note: If you forget ACK, the consequences will be very serious . There initiate ack will lead to the accumulation of messages on the server has the server will send a new message will also record the current news of this link which has yet to reply (server thinks. you will reply, has been waiting for) . If consumers stop off to restart the process again .. will receive all the news! Then Rabbitmq will take up more and more memory, because the Rabbitmq long run, this will result in a "memory leak";


Third Transaction:

The message is set to persist is not guaranteed and will not be lost. Persistent just want to tell the news RabbitMq save to your hard drive, but RabbitMq receive a message to save between there are a very small time interval. Because RabbitMq not all messages are synchronized using IO- it might just be saved to the cache, not necessarily written to the hard disk. It does not guarantee real persistence, but sufficient to meet our simple work queue. If you must ensure persistence, we need to rewrite code to support a transaction (transaction).

Producers mode affairs:

Services will determine the message to disk before submitting;

Consumer model using transactions

Consumer model assumptions used in the transaction, and the transaction has been rolled back after the news confirmed, RabbitMQ will produce what kind of change?

The results are divided into two cases:

  1. autoAck = false when manual response is transactional, meaning that even if you have to manually confirm the message has been received, but after a confirmation message will be returned to resolve other matters, the decision is a confirmation message, or back into the queue, If you manually confirm later now, and roll back the transaction, the transaction has been rolled back the main message this entry back into the queue;
  2. autoAck = true if custom is recognized as the true situation does not support transactions, which means that even after you receive the message in rolling back a transaction does not help, and the message queue has been removed;

Transaction Code:

/**
 * 生产者事务
 *
 * @date 2019-06-18
 */
public function actionProducerTransaction()
{
    $channel = RabbitmqBase::createChannel();
    $channel->exchange_declare('exchange_transaction', 'direct', false, true,
        false);//这个是申明交换器,如果没有申明就给默认队列的这个交换器,而且发送的类型默认是direct)
    $channel->queue_declare('TQ1', false, true, false, false, false);
    $channel->queue_bind('TQ1', 'exchange_transaction');
    $channel->tx_select();
    try {
        // 发送消息
        $newMsg = new AMQPMessage('test_transaction',
            [
                'delivery_mode' => 1,
                'message_id' => uniqid(),
            ]);
        $channel->basic_publish($newMsg, 'exchange_transaction'); //这里的queue是消息名称
        $channel->tx_commit();
    } catch (Exception $e) {
        echo sprintf("失败%s。。。\n", $e->getMessage());
        $channel->tx_rollback();
    } finally {
        $channel->close();
        RabbitmqBase::closeConnection();
    }
}

/**
 * 消费者事务:
 */
public function actionConsumeTransaction()
{
    $channel = RabbitmqBase::createChannel();
    $channel->tx_select();
    try {
        $channel->basic_qos(null, 1, null);
        $msg = $channel->basic_get('AQ1', false);
        if ($msg) {
            echo " [x] Received ", $msg->body, "\n";
            //处理消息之后确认(一定要却懂开启了消息确认机制才能用这个)
            $channel->basic_ack($msg->delivery_info['delivery_tag']);
        } else {
            throw new UserException('未收到消息');
        }
        $channel->tx_commit();
    } catch (Exception $e) {
        $channel->tx_rollback();
    } finally {
        $channel->close();
        RabbitmqBase::closeConnection();
    }
}

Confirm mode

Outline

Above us a question RabbitMQ might encounter, which generates does not know whether the message actually reaches the broker, followed by AMQP protocol provides transaction-level mechanism to solve this problem for us, but the use of mechanisms to achieve the transaction will reduce the RabbitMQ messaging throughput, then there is no more efficient way to solve it? The answer is to use Confirm mode.

The principle of producer end confirm mode

Producers channel set to confirm mode, once the channel entering confirm mode, all published on the channel surface message will be assigned a unique ID (starting at 1), once the message is delivered to all matching queue, Broker will sends an acknowledgment to the producer (the unique ID contained in the message), which message has the right to know the manufacturers reach the destination queue, and the queue if the message is persistent, the acknowledgment message will be sent after the message is written to disk, acknowledgment message back to the broker in the producer deliver-tag field contains the sequence number acknowledgment message, the broker may be provided in addition to multiple basic.ack field that indicates all messages before the serial number have been processed.

The biggest advantage is that he is an asynchronous mode confirm, once posted a message producer application can return a message channel at the same time continue to send confirmation of waiting, when the news finally confirmed, the producer will be able to use a callback method to process the confirmation message, if RabbitMQ because of their own internal error message is lost, it will send a nack message producer application can also handle the nack message callback method.

After the channel mode is set to confirm, publish all subsequent messages will be confirm (i.e. ack) or is a nack. But it did not make any guarantee to confirm the message was speed, and will not only be nack confirm again the same message.

Open confirm mode method

Producers by confirmSelect method call channel of the channel is set to confirm mode, if no-wait flag is not set, then, broker will return confirm.select-ok agreed the sender of the current channel is set to confirm channel mode (from the latest version of RabbitMQ 3.6 of view, if you call a channel.confirmSelect method, by default, is the direct no-wait is set to false, which is the default broker must return confirm.select-ok's).

confirm the code:

/**
 * producer端confirm模式
 *
 * @date 2019-06-18
 */
public function actionProducerConfirm()
{
    $channel = RabbitmqBase::createChannel();
    $channel->exchange_declare('exchange_confirm', 'direct', false, true,
        false);//这个是申明交换器,如果没有申明就给默认队列的这个交换器,而且发送的类型默认是direct)
    $channel->queue_declare('CQ1', false, true, false, false, false);
    $channel->queue_bind('CQ1', 'exchange_confirm');
    try {
        //设置异步回调消息确认 (生产者 防止信息丢失)
        $channel->set_ack_handler(
            function (AMQPMessage $message) {
                echo "Message acked with content " . $message->body . PHP_EOL;
                sleep(1);
            }
        );
        //消息失败后处理方式
        $channel->set_nack_handler(
            function (AMQPMessage $message) {
                throw new UserException($message->body);
            }
        );
        //选择为 confirm 模式(此模式不可以和事务模式 兼容)
        $channel->confirm_select();
        // 发送消息
        $newMsg = new AMQPMessage('hello test_confirm',
            [
                'delivery_mode' => 2,
                'message_id' => uniqid(),
            ]);
        $channel->basic_publish($newMsg, 'exchange_confirm'); //这里的queue是消息名称

        //阻塞等待消息确认 (生产者 防止信息丢失)
        $channel->wait_for_pending_acks();
    } catch (Exception $e) {
        echo sprintf("失败%s。。。\n", $e->getMessage());
    } finally {
        $channel->close();
        RabbitmqBase::closeConnection();
    }
}
public function actionProducerConfirms()
{
    $channel = RabbitmqBase::createChannel();
    $channel->exchange_declare('exchange_confirm', 'direct', false, true,
        false);//这个是申明交换器,如果没有申明就给默认队列的这个交换器,而且发送的类型默认是direct)
    $channel->queue_declare('CQ1', false, true, false, false, false);
    $channel->queue_bind('CQ1', 'exchange_confirm');
    try {
        //设置异步回调消息确认 (生产者 防止信息丢失)
        $channel->set_ack_handler(
            function (AMQPMessage $message) {
                echo "Message acked with content " . $message->body . PHP_EOL;
                sleep(1);
            }
        );
        //消息失败后处理方式
        $channel->set_nack_handler(
            function (AMQPMessage $message) {
                throw new UserException($message->body);
            }
        );
        //选择为 confirm 模式(此模式不可以和事务模式 兼容)
        $channel->confirm_select();
        // 发送消息
        $newMsg = new AMQPMessage('hello test_confirm',
            [
                'delivery_mode' => 2,
                'message_id' => uniqid(),
            ]);
        $channel->basic_publish($newMsg, 'exchange_confirm'); //这里的queue是消息名称

        //阻塞等待消息确认 (生产者 防止信息丢失)
        $channel->wait_for_pending_acks_returns();
    } catch (Exception $e) {
        echo sprintf("失败%s。。。\n", $e->getMessage());
    } finally {
        $channel->close();
        RabbitmqBase::closeConnection();
    }
}

 

Guess you like

Origin blog.csdn.net/qq_38234594/article/details/93382476